From 262061086a9633651ea1d7d4ae5840e814af80da Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 12 Sep 2021 13:30:50 -0700 Subject: [PATCH 1/7] add upstream bevy_ecs --- crates/bevy_ecs/Cargo.toml | 4 - crates/bevy_ecs/README.md | 2 +- crates/bevy_ecs/examples/change_detection.rs | 14 +- crates/bevy_ecs/examples/component_storage.rs | 2 +- crates/bevy_ecs/examples/events.rs | 6 +- crates/bevy_ecs/examples/resources.rs | 4 +- crates/bevy_ecs/macros/src/lib.rs | 127 +++-- crates/bevy_ecs/src/archetype.rs | 42 +- crates/bevy_ecs/src/bundle.rs | 435 ++++++++++++++++-- .../src/{component/mod.rs => component.rs} | 110 ++--- crates/bevy_ecs/src/component/type_info.rs | 63 --- crates/bevy_ecs/src/entity/mod.rs | 64 ++- crates/bevy_ecs/src/event.rs | 15 +- crates/bevy_ecs/src/lib.rs | 118 ++++- crates/bevy_ecs/src/query/fetch.rs | 17 + crates/bevy_ecs/src/query/filter.rs | 103 +---- crates/bevy_ecs/src/query/iter.rs | 35 +- crates/bevy_ecs/src/query/state.rs | 144 ++++-- crates/bevy_ecs/src/reflect.rs | 8 + .../src/schedule/executor_parallel.rs | 48 +- crates/bevy_ecs/src/schedule/label.rs | 110 ++++- crates/bevy_ecs/src/schedule/mod.rs | 7 +- crates/bevy_ecs/src/schedule/run_criteria.rs | 22 +- crates/bevy_ecs/src/schedule/stage.rs | 373 +++++++-------- crates/bevy_ecs/src/schedule/state.rs | 117 ++--- .../bevy_ecs/src/schedule/system_container.rs | 4 +- .../src/schedule/system_descriptor.rs | 10 +- crates/bevy_ecs/src/storage/blob_vec.rs | 12 +- crates/bevy_ecs/src/storage/table.rs | 9 +- crates/bevy_ecs/src/system/commands/mod.rs | 103 ++++- .../bevy_ecs/src/system/exclusive_system.rs | 7 +- crates/bevy_ecs/src/system/function_system.rs | 18 +- crates/bevy_ecs/src/system/mod.rs | 326 +++++++++++-- crates/bevy_ecs/src/system/param_state.rs | 36 -- crates/bevy_ecs/src/system/query.rs | 124 +++-- crates/bevy_ecs/src/system/system.rs | 2 +- crates/bevy_ecs/src/system/system_param.rs | 93 ++-- crates/bevy_ecs/src/world/entity_ref.rs | 316 ++++--------- crates/bevy_ecs/src/world/mod.rs | 139 +++++- crates/bevy_ecs/src/world/pointer.rs | 1 - crates/bevy_ecs/src/world/spawn_batch.rs | 76 +-- crates/bevy_ecs/src/world/world_cell.rs | 2 +- crates/bevy_render/Cargo.toml | 4 - crates/bevy_render/src/camera/camera.rs | 9 +- crates/bevy_render/src/color.rs | 7 +- crates/bevy_render/src/draw.rs | 4 +- crates/bevy_render/src/lib.rs | 42 +- crates/bevy_render/src/mesh/mesh.rs | 11 +- .../src/render_graph/nodes/camera_node.rs | 4 +- .../nodes/render_resources_node.rs | 49 +- .../bevy_render/src/shader/shader_reflect.rs | 14 +- crates/bevy_render/src/wireframe/mod.rs | 14 +- crates/bevy_text/Cargo.toml | 4 - crates/bevy_text/src/lib.rs | 6 +- crates/bevy_text/src/text2d.rs | 10 +- crates/bevy_ui/Cargo.toml | 4 - crates/bevy_ui/src/flex/convert.rs | 4 +- crates/bevy_ui/src/lib.rs | 18 +- crates/bevy_ui/src/ui_node.rs | 4 +- crates/bevy_ui/src/update.rs | 4 +- crates/bevy_ui/src/widget/text.rs | 10 +- pipelined/bevy_render2/src/camera/camera.rs | 10 +- 62 files changed, 2034 insertions(+), 1466 deletions(-) rename crates/bevy_ecs/src/{component/mod.rs => component.rs} (82%) delete mode 100644 crates/bevy_ecs/src/component/type_info.rs delete mode 100644 crates/bevy_ecs/src/system/param_state.rs delete mode 100644 crates/bevy_ecs/src/world/pointer.rs diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index b7797993bc427..00010a15252e0 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -2,10 +2,6 @@ name = "bevy_ecs" version = "0.5.0" edition = "2018" -authors = [ - "Bevy Contributors ", - "Carter Anderson ", -] description = "Bevy Engine's entity component system" homepage = "https://bevyengine.org" repository = "https://github.com/bevyengine/bevy" diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index ef1d39644850e..478b23b915a1f 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -131,7 +131,7 @@ fn main() { // Add a Stage to our schedule. Each Stage in a schedule runs all of its systems // before moving on to the next Stage schedule.add_stage("update", SystemStage::parallel() - .with_system(movement.system()) + .with_system(movement) ); // Run the schedule once. If your app has a "loop", you would run this once per loop diff --git a/crates/bevy_ecs/examples/change_detection.rs b/crates/bevy_ecs/examples/change_detection.rs index 97ece09e9e2a9..ec80dcb9393aa 100644 --- a/crates/bevy_ecs/examples/change_detection.rs +++ b/crates/bevy_ecs/examples/change_detection.rs @@ -24,15 +24,11 @@ fn main() { // Add systems to the Stage to execute our app logic // We can label our systems to force a specific run-order between some of them - update.add_system(spawn_entities.system().label(SimulationSystem::Spawn)); - update.add_system( - print_counter_when_changed - .system() - .after(SimulationSystem::Spawn), - ); - update.add_system(age_all_entities.system().label(SimulationSystem::Age)); - update.add_system(remove_old_entities.system().after(SimulationSystem::Age)); - update.add_system(print_changed_entities.system().after(SimulationSystem::Age)); + update.add_system(spawn_entities.label(SimulationSystem::Spawn)); + update.add_system(print_counter_when_changed.after(SimulationSystem::Spawn)); + update.add_system(age_all_entities.label(SimulationSystem::Age)); + update.add_system(remove_old_entities.after(SimulationSystem::Age)); + update.add_system(print_changed_entities.after(SimulationSystem::Age)); // Add the Stage with our systems to the Schedule schedule.add_stage("update", update); diff --git a/crates/bevy_ecs/examples/component_storage.rs b/crates/bevy_ecs/examples/component_storage.rs index 95619a6455dd3..018f7afd4750c 100644 --- a/crates/bevy_ecs/examples/component_storage.rs +++ b/crates/bevy_ecs/examples/component_storage.rs @@ -22,7 +22,7 @@ fn main() { // Setup a schedule and stage to add a system querying for the just spawned entities let mut schedule = Schedule::default(); let mut update = SystemStage::parallel(); - update.add_system(query_entities.system()); + update.add_system(query_entities); schedule.add_stage("update", update); schedule.run(&mut world); diff --git a/crates/bevy_ecs/examples/events.rs b/crates/bevy_ecs/examples/events.rs index a172081cf7344..a90510526f002 100644 --- a/crates/bevy_ecs/examples/events.rs +++ b/crates/bevy_ecs/examples/events.rs @@ -16,13 +16,13 @@ fn main() { // called "second". In "first" we update the events and in "second" we run our systems // sending and receiving events. let mut first = SystemStage::parallel(); - first.add_system(Events::::update_system.system()); + first.add_system(Events::::update_system); schedule.add_stage("first", first); // Add systems sending and receiving events to a "second" Stage let mut second = SystemStage::parallel(); - second.add_system(sending_system.system().label(EventSystem::Sending)); - second.add_system(receiving_system.system().after(EventSystem::Sending)); + second.add_system(sending_system.label(EventSystem::Sending)); + second.add_system(receiving_system.after(EventSystem::Sending)); // Run the "second" Stage after the "first" Stage, so our Events always get updated before we use them schedule.add_stage_after("first", "second", second); diff --git a/crates/bevy_ecs/examples/resources.rs b/crates/bevy_ecs/examples/resources.rs index c898a3a7e9d40..9bc40b5bb3f94 100644 --- a/crates/bevy_ecs/examples/resources.rs +++ b/crates/bevy_ecs/examples/resources.rs @@ -16,8 +16,8 @@ fn main() { let mut update = SystemStage::parallel(); // Add systems to increase the counter and to print out the current value - update.add_system(increase_counter.system().label(CounterSystem::Increase)); - update.add_system(print_counter.system().after(CounterSystem::Increase)); + update.add_system(increase_counter.label(CounterSystem::Increase)); + update.add_system(print_counter.after(CounterSystem::Increase)); schedule.add_stage("update", update); for iteration in 1..=10 { diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index cc88f89ddcf55..8371de3e4b160 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -1,16 +1,16 @@ extern crate proc_macro; -use bevy_macro_utils::{derive_label, BevyManifest}; +use bevy_macro_utils::BevyManifest; use proc_macro::TokenStream; -use proc_macro2::Span; +use proc_macro2::{Span, TokenStream as TokenStream2}; use quote::{format_ident, quote}; use syn::{ parse::{Parse, ParseStream}, parse_macro_input, punctuated::Punctuated, token::Comma, - Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt, - Result, Token, + Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result, + Token, }; struct AllTuples { @@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { .map(|field| &field.ty) .collect::>(); - let mut field_type_infos = Vec::new(); + let mut field_component_ids = Vec::new(); let mut field_get_components = Vec::new(); let mut field_from_components = Vec::new(); for ((field_type, is_bundle), field) in field_type.iter().zip(is_bundle.iter()).zip(field.iter()) { if *is_bundle { - field_type_infos.push(quote! { - type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info()); + field_component_ids.push(quote! { + component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components)); }); field_get_components.push(quote! { self.#field.get_components(&mut func); @@ -127,8 +127,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func), }); } else { - field_type_infos.push(quote! { - type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>()); + field_component_ids.push(quote! { + component_ids.push(components.get_or_insert_id::<#field_type>()); }); field_get_components.push(quote! { func((&mut self.#field as *mut #field_type).cast::()); @@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream { TokenStream::from(quote! { /// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause { - fn type_info() -> Vec<#ecs_path::component::TypeInfo> { - let mut type_info = Vec::with_capacity(#field_len); - #(#field_type_infos)* - type_info + fn component_ids( + components: &mut #ecs_path::component::Components, + ) -> Vec<#ecs_path::component::ComponentId> { + let mut component_ids = Vec::with_capacity(#field_len); + #(#field_component_ids)* + component_ids } #[allow(unused_variables, unused_mut, non_snake_case)] @@ -174,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) + } } }); } @@ -213,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<'s, #(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, 's, #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>,)*)>; @@ -271,10 +258,10 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { fn default_config() {} } - impl<'s, 'w, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'s, 'w> 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<'w, 's, #query, #filter>,)*)>; + type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>; #[inline] unsafe fn get_param( @@ -283,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<#(#state_lifetime,)* #(#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)* } })); @@ -385,7 +375,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { TokenStream::from(quote! { impl #impl_generics #path::system::SystemParam for #struct_name#ty_generics #where_clause { - type Fetch = #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents>; + type Fetch = #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>; } #[doc(hidden)] @@ -416,7 +406,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { } } - impl #impl_generics #path::system::SystemParamFetch<'s, 'w> for #fetch_struct_name <(#(<#field_types as 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: &'s mut Self, @@ -425,7 +415,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { change_tick: u32, ) -> Self::Item { #struct_name { - #(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)* + #(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)* #(#ignored_fields: <#ignored_field_types>::default(),)* } } @@ -436,43 +426,46 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { #[proc_macro_derive(SystemLabel)] pub fn derive_system_label(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let mut trait_path = bevy_ecs_path(); - trait_path.segments.push(format_ident!("schedule").into()); - trait_path - .segments - .push(format_ident!("SystemLabel").into()); - derive_label(input, trait_path) + + derive_label(input, Ident::new("SystemLabel", Span::call_site())).into() } #[proc_macro_derive(StageLabel)] pub fn derive_stage_label(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let mut trait_path = bevy_ecs_path(); - trait_path.segments.push(format_ident!("schedule").into()); - trait_path.segments.push(format_ident!("StageLabel").into()); - derive_label(input, trait_path) + derive_label(input, Ident::new("StageLabel", Span::call_site())).into() } #[proc_macro_derive(AmbiguitySetLabel)] pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let mut trait_path = bevy_ecs_path(); - trait_path.segments.push(format_ident!("schedule").into()); - trait_path - .segments - .push(format_ident!("AmbiguitySetLabel").into()); - derive_label(input, trait_path) + derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into() } #[proc_macro_derive(RunCriteriaLabel)] pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let mut trait_path = bevy_ecs_path(); - trait_path.segments.push(format_ident!("schedule").into()); - trait_path - .segments - .push(format_ident!("RunCriteriaLabel").into()); - derive_label(input, trait_path) + derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into() +} + +fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 { + let ident = input.ident; + let ecs_path: Path = bevy_ecs_path(); + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause { + where_token: Default::default(), + predicates: Default::default(), + }); + where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap()); + + quote! { + impl #impl_generics #ecs_path::schedule::#label_type for #ident #ty_generics #where_clause { + fn dyn_clone(&self) -> Box { + Box::new(Clone::clone(self)) + } + } + } } fn bevy_ecs_path() -> syn::Path { diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index b213660cd703d..d2ed1da1ac172 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -15,26 +15,15 @@ use std::{ pub struct ArchetypeId(usize); impl ArchetypeId { + pub const EMPTY: ArchetypeId = ArchetypeId(0); + pub const RESOURCE: ArchetypeId = ArchetypeId(1); + pub const INVALID: ArchetypeId = ArchetypeId(usize::MAX); + #[inline] pub const fn new(index: usize) -> Self { ArchetypeId(index) } - #[inline] - pub const fn empty() -> ArchetypeId { - ArchetypeId(0) - } - - #[inline] - pub const fn invalid() -> ArchetypeId { - ArchetypeId(usize::MAX) - } - - #[inline] - pub const fn resource() -> ArchetypeId { - ArchetypeId(1) - } - #[inline] pub fn index(self) -> usize { self.0 @@ -65,7 +54,7 @@ impl Edges { } #[inline] - pub fn set_add_bundle( + pub fn insert_add_bundle( &mut self, bundle_id: BundleId, archetype_id: ArchetypeId, @@ -86,7 +75,7 @@ impl Edges { } #[inline] - pub fn set_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option) { + pub fn insert_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option) { self.remove_bundle.insert(bundle_id, archetype_id); } @@ -99,7 +88,7 @@ impl Edges { } #[inline] - pub fn set_remove_bundle_intersection( + pub fn insert_remove_bundle_intersection( &mut self, bundle_id: BundleId, archetype_id: Option, @@ -115,8 +104,8 @@ struct TableInfo { } pub(crate) struct ArchetypeSwapRemoveResult { - pub swapped_entity: Option, - pub table_row: usize, + pub(crate) swapped_entity: Option, + pub(crate) table_row: usize, } pub(crate) struct ArchetypeComponentInfo { @@ -387,7 +376,7 @@ impl Default for Archetypes { // adds the resource archetype. it is "special" in that it is inaccessible via a "hash", // which prevents entities from being added to it archetypes.archetypes.push(Archetype::new( - ArchetypeId::resource(), + ArchetypeId::RESOURCE, TableId::empty(), Cow::Owned(Vec::new()), Cow::Owned(Vec::new()), @@ -412,7 +401,7 @@ impl Archetypes { #[inline] pub fn empty(&self) -> &Archetype { // SAFE: empty archetype always exists - unsafe { self.archetypes.get_unchecked(ArchetypeId::empty().index()) } + unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) } } #[inline] @@ -420,17 +409,14 @@ impl Archetypes { // SAFE: empty archetype always exists unsafe { self.archetypes - .get_unchecked_mut(ArchetypeId::empty().index()) + .get_unchecked_mut(ArchetypeId::EMPTY.index()) } } #[inline] pub fn resource(&self) -> &Archetype { // SAFE: resource archetype always exists - unsafe { - self.archetypes - .get_unchecked(ArchetypeId::resource().index()) - } + unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) } } #[inline] @@ -438,7 +424,7 @@ impl Archetypes { // SAFE: resource archetype always exists unsafe { self.archetypes - .get_unchecked_mut(ArchetypeId::resource().index()) + .get_unchecked_mut(ArchetypeId::RESOURCE.index()) } } diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 8a9f2f15e461d..432a3d11e66cb 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -1,10 +1,10 @@ pub use bevy_ecs_macros::Bundle; use crate::{ - archetype::ComponentStatus, - component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo}, - entity::Entity, - storage::{SparseSetIndex, SparseSets, Table}, + archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus}, + component::{Component, ComponentId, ComponentTicks, Components, StorageType}, + entity::{Entities, Entity, EntityLocation}, + storage::{SparseSetIndex, SparseSets, Storages, Table}, }; use bevy_ecs_macros::all_tuples; use std::{any::TypeId, collections::HashMap}; @@ -12,8 +12,6 @@ use std::{any::TypeId, collections::HashMap}; /// An ordered collection of components, commonly used for spawning entities, and adding and /// removing components in bulk. /// -/// In order to query for components in a bundle use [crate::query::WithBundle]. -/// /// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`. /// The `Bundle` trait is automatically implemented for tuples of components: /// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections @@ -38,13 +36,13 @@ use std::{any::TypeId, collections::HashMap}; /// ``` /// /// # Safety -/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the +/// [Bundle::component_id] must return the ComponentId for each component type in the bundle, in the /// _exact_ order that [Bundle::get_components] is called. -/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by -/// [Bundle::type_info] +/// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by +/// [Bundle::component_id] pub unsafe trait Bundle: Send + Sync + 'static { - /// Gets this [Bundle]'s components type info, in the order of this bundle's Components - fn type_info() -> Vec; + /// Gets this [Bundle]'s component ids, in the order of this bundle's Components + fn component_ids(components: &mut Components) -> Vec; /// Calls `func`, which should return data for each component in the bundle, in the order of /// this bundle's Components @@ -66,8 +64,9 @@ macro_rules! tuple_impl { ($($name: ident),*) => { /// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { - fn type_info() -> Vec { - vec![$(TypeInfo::of::<$name>()),*] + #[allow(unused_variables)] + fn component_ids(components: &mut Components) -> Vec { + vec![$(components.get_or_insert_id::<$name>()),*] } #[allow(unused_variables, unused_mut)] @@ -123,19 +122,119 @@ pub struct BundleInfo { } impl BundleInfo { + #[inline] + pub fn id(&self) -> BundleId { + self.id + } + + #[inline] + pub fn components(&self) -> &[ComponentId] { + &self.component_ids + } + + #[inline] + pub fn storage_types(&self) -> &[StorageType] { + &self.storage_types + } + + pub(crate) fn get_bundle_inserter<'a, 'b>( + &'b self, + entities: &'a mut Entities, + archetypes: &'a mut Archetypes, + components: &mut Components, + storages: &'a mut Storages, + archetype_id: ArchetypeId, + change_tick: u32, + ) -> BundleInserter<'a, 'b> { + let new_archetype_id = + self.add_bundle_to_archetype(archetypes, storages, components, archetype_id); + let archetypes_ptr = archetypes.archetypes.as_mut_ptr(); + if new_archetype_id == archetype_id { + let archetype = &mut archetypes[archetype_id]; + let table_id = archetype.table_id(); + BundleInserter { + bundle_info: self, + archetype, + entities, + sparse_sets: &mut storages.sparse_sets, + table: &mut storages.tables[table_id], + archetypes_ptr, + change_tick, + result: InsertBundleResult::SameArchetype, + } + } else { + let (archetype, new_archetype) = archetypes.get_2_mut(archetype_id, new_archetype_id); + let table_id = archetype.table_id(); + if table_id == new_archetype.table_id() { + BundleInserter { + bundle_info: self, + archetype, + archetypes_ptr, + entities, + sparse_sets: &mut storages.sparse_sets, + table: &mut storages.tables[table_id], + change_tick, + result: InsertBundleResult::NewArchetypeSameTable { new_archetype }, + } + } else { + let (table, new_table) = storages + .tables + .get_2_mut(table_id, new_archetype.table_id()); + BundleInserter { + bundle_info: self, + archetype, + sparse_sets: &mut storages.sparse_sets, + entities, + archetypes_ptr, + table, + change_tick, + result: InsertBundleResult::NewArchetypeNewTable { + new_archetype, + new_table, + }, + } + } + } + } + + pub(crate) fn get_bundle_spawner<'a, 'b>( + &'b self, + entities: &'a mut Entities, + archetypes: &'a mut Archetypes, + components: &mut Components, + storages: &'a mut Storages, + change_tick: u32, + ) -> BundleSpawner<'a, 'b> { + let new_archetype_id = + self.add_bundle_to_archetype(archetypes, storages, components, ArchetypeId::EMPTY); + let (empty_archetype, archetype) = + archetypes.get_2_mut(ArchetypeId::EMPTY, new_archetype_id); + let table = &mut storages.tables[archetype.table_id()]; + let add_bundle = empty_archetype.edges().get_add_bundle(self.id()).unwrap(); + BundleSpawner { + archetype, + add_bundle, + bundle_info: self, + table, + entities, + sparse_sets: &mut storages.sparse_sets, + change_tick, + } + } + /// # Safety - /// table row must exist, entity must be valid - #[allow(clippy::too_many_arguments)] + /// `table` must be the "new" table for `entity`. `table_row` must have space allocated for the `entity`, `bundle` must match this BundleInfo's type #[inline] - pub(crate) unsafe fn write_components( + #[allow(clippy::too_many_arguments)] + unsafe fn write_components( &self, + table: &mut Table, sparse_sets: &mut SparseSets, + add_bundle: &AddBundle, entity: Entity, - table: &mut Table, table_row: usize, - bundle_status: &[ComponentStatus], - bundle: T, change_tick: u32, + bundle: T, ) { // NOTE: get_components calls this closure on each component in "bundle order". // bundle_info.component_ids are also in "bundle order" @@ -145,7 +244,7 @@ impl BundleInfo { match self.storage_types[bundle_component] { StorageType::Table => { let column = table.get_column_mut(component_id).unwrap(); - match bundle_status.get_unchecked(bundle_component) { + match add_bundle.bundle_status.get_unchecked(bundle_component) { ComponentStatus::Added => { column.initialize( table_row, @@ -167,19 +266,277 @@ impl BundleInfo { }); } + /// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same + /// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change. + /// Results are cached in the Archetype Graph to avoid redundant work. + pub(crate) fn add_bundle_to_archetype( + &self, + archetypes: &mut Archetypes, + storages: &mut Storages, + components: &mut Components, + archetype_id: ArchetypeId, + ) -> ArchetypeId { + if let Some(add_bundle) = archetypes[archetype_id].edges().get_add_bundle(self.id) { + return add_bundle.archetype_id; + } + let mut new_table_components = Vec::new(); + let mut new_sparse_set_components = Vec::new(); + let mut bundle_status = Vec::with_capacity(self.component_ids.len()); + + let current_archetype = &mut archetypes[archetype_id]; + for component_id in self.component_ids.iter().cloned() { + if current_archetype.contains(component_id) { + bundle_status.push(ComponentStatus::Mutated); + } else { + bundle_status.push(ComponentStatus::Added); + // SAFE: component_id exists + let component_info = unsafe { components.get_info_unchecked(component_id) }; + match component_info.storage_type() { + StorageType::Table => new_table_components.push(component_id), + StorageType::SparseSet => { + storages.sparse_sets.get_or_insert(component_info); + new_sparse_set_components.push(component_id) + } + } + } + } + + if new_table_components.is_empty() && new_sparse_set_components.is_empty() { + let edges = current_archetype.edges_mut(); + // the archetype does not change when we add this bundle + edges.insert_add_bundle(self.id, archetype_id, bundle_status); + archetype_id + } else { + let table_id; + let table_components; + let sparse_set_components; + // the archetype changes when we add this bundle. prepare the new archetype and storages + { + let current_archetype = &archetypes[archetype_id]; + table_components = if new_table_components.is_empty() { + // if there are no new table components, we can keep using this table + table_id = current_archetype.table_id(); + current_archetype.table_components().to_vec() + } else { + new_table_components.extend(current_archetype.table_components()); + // sort to ignore order while hashing + new_table_components.sort(); + // SAFE: all component ids in `new_table_components` exist + table_id = unsafe { + storages + .tables + .get_id_or_insert(&new_table_components, components) + }; + + new_table_components + }; + + sparse_set_components = if new_sparse_set_components.is_empty() { + current_archetype.sparse_set_components().to_vec() + } else { + new_sparse_set_components.extend(current_archetype.sparse_set_components()); + // sort to ignore order while hashing + new_sparse_set_components.sort(); + new_sparse_set_components + }; + }; + let new_archetype_id = + archetypes.get_id_or_insert(table_id, table_components, sparse_set_components); + // add an edge from the old archetype to the new archetype + archetypes[archetype_id].edges_mut().insert_add_bundle( + self.id, + new_archetype_id, + bundle_status, + ); + new_archetype_id + } + } +} + +pub(crate) struct BundleInserter<'a, 'b> { + pub(crate) archetype: &'a mut Archetype, + pub(crate) entities: &'a mut Entities, + bundle_info: &'b BundleInfo, + table: &'a mut Table, + sparse_sets: &'a mut SparseSets, + result: InsertBundleResult<'a>, + archetypes_ptr: *mut Archetype, + change_tick: u32, +} + +pub(crate) enum InsertBundleResult<'a> { + SameArchetype, + NewArchetypeSameTable { + new_archetype: &'a mut Archetype, + }, + NewArchetypeNewTable { + new_archetype: &'a mut Archetype, + new_table: &'a mut Table, + }, +} + +impl<'a, 'b> BundleInserter<'a, 'b> { + /// # Safety + /// `entity` must currently exist in the source archetype for this inserter. `archetype_index` must be `entity`'s location in the archetype. + /// `T` must match this BundleInfo's type #[inline] - pub fn id(&self) -> BundleId { - self.id + pub unsafe fn insert( + &mut self, + entity: Entity, + archetype_index: usize, + bundle: T, + ) -> EntityLocation { + let location = EntityLocation { + index: archetype_index, + archetype_id: self.archetype.id(), + }; + match &mut self.result { + InsertBundleResult::SameArchetype => { + // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) + let add_bundle = self + .archetype + .edges() + .get_add_bundle(self.bundle_info.id) + .unwrap(); + self.bundle_info.write_components( + self.table, + self.sparse_sets, + add_bundle, + entity, + self.archetype.entity_table_row(archetype_index), + self.change_tick, + bundle, + ); + location + } + InsertBundleResult::NewArchetypeSameTable { new_archetype } => { + let result = self.archetype.swap_remove(location.index); + if let Some(swapped_entity) = result.swapped_entity { + self.entities.meta[swapped_entity.id as usize].location = location; + } + let new_location = new_archetype.allocate(entity, result.table_row); + self.entities.meta[entity.id as usize].location = new_location; + + // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) + let add_bundle = self + .archetype + .edges() + .get_add_bundle(self.bundle_info.id) + .unwrap(); + self.bundle_info.write_components( + self.table, + self.sparse_sets, + add_bundle, + entity, + result.table_row, + self.change_tick, + bundle, + ); + new_location + } + InsertBundleResult::NewArchetypeNewTable { + new_archetype, + new_table, + } => { + let result = self.archetype.swap_remove(location.index); + if let Some(swapped_entity) = result.swapped_entity { + self.entities.meta[swapped_entity.id as usize].location = location; + } + // PERF: store "non bundle" components in edge, then just move those to avoid + // redundant copies + let move_result = self + .table + .move_to_superset_unchecked(result.table_row, &mut *new_table); + let new_location = new_archetype.allocate(entity, move_result.new_row); + self.entities.meta[entity.id as usize].location = new_location; + + // if an entity was moved into this entity's table spot, update its table row + if let Some(swapped_entity) = move_result.swapped_entity { + let swapped_location = self.entities.get(swapped_entity).unwrap(); + let swapped_archetype = if self.archetype.id() == swapped_location.archetype_id + { + &mut *self.archetype + } else if new_archetype.id() == swapped_location.archetype_id { + &mut *new_archetype + } else { + // SAFE: the only two borrowed archetypes are above and we just did collision checks + &mut *self + .archetypes_ptr + .add(swapped_location.archetype_id.index()) + }; + + swapped_archetype + .set_entity_table_row(swapped_location.index, result.table_row); + } + + // PERF: this could be looked up during Inserter construction and stored (but borrowing makes this nasty) + let add_bundle = self + .archetype + .edges() + .get_add_bundle(self.bundle_info.id) + .unwrap(); + self.bundle_info.write_components( + new_table, + self.sparse_sets, + add_bundle, + entity, + move_result.new_row, + self.change_tick, + bundle, + ); + new_location + } + } } +} +pub(crate) struct BundleSpawner<'a, 'b> { + pub(crate) archetype: &'a mut Archetype, + pub(crate) entities: &'a mut Entities, + add_bundle: &'a AddBundle, + bundle_info: &'b BundleInfo, + table: &'a mut Table, + sparse_sets: &'a mut SparseSets, + change_tick: u32, +} + +impl<'a, 'b> BundleSpawner<'a, 'b> { + pub fn reserve_storage(&mut self, additional: usize) { + self.archetype.reserve(additional); + self.table.reserve(additional); + } + /// # Safety + /// `entity` must be allocated (but non existent), `T` must match this BundleInfo's type #[inline] - pub fn components(&self) -> &[ComponentId] { - &self.component_ids + pub unsafe fn spawn_non_existent( + &mut self, + entity: Entity, + bundle: T, + ) -> EntityLocation { + let table_row = self.table.allocate(entity); + let location = self.archetype.allocate(entity, table_row); + self.bundle_info.write_components( + self.table, + self.sparse_sets, + self.add_bundle, + entity, + table_row, + self.change_tick, + bundle, + ); + self.entities.meta[entity.id as usize].location = location; + + location } + /// # Safety + /// `T` must match this BundleInfo's type #[inline] - pub fn storage_types(&self) -> &[StorageType] { - &self.storage_types + pub unsafe fn spawn(&mut self, bundle: T) -> Entity { + let entity = self.entities.alloc(); + // SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type + self.spawn_non_existent(entity, bundle); + entity } } @@ -206,10 +563,12 @@ impl Bundles { ) -> &'a BundleInfo { let bundle_infos = &mut self.bundle_infos; let id = self.bundle_ids.entry(TypeId::of::()).or_insert_with(|| { - let type_info = T::type_info(); + let component_ids = T::component_ids(components); let id = BundleId(bundle_infos.len()); - let bundle_info = - initialize_bundle(std::any::type_name::(), &type_info, id, components); + // SAFE: T::component_id ensures info was created + let bundle_info = unsafe { + initialize_bundle(std::any::type_name::(), component_ids, id, components) + }; bundle_infos.push(bundle_info); id }); @@ -218,21 +577,21 @@ impl Bundles { } } -fn initialize_bundle( +/// # Safety +/// +/// `component_id` must be valid [ComponentId]'s +unsafe fn initialize_bundle( bundle_type_name: &'static str, - type_info: &[TypeInfo], + component_ids: Vec, id: BundleId, components: &mut Components, ) -> BundleInfo { - let mut component_ids = Vec::new(); let mut storage_types = Vec::new(); - for type_info in type_info { - let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone()); - // SAFE: get_with_type_info ensures info was created - let info = unsafe { components.get_info_unchecked(component_id) }; - component_ids.push(component_id); - storage_types.push(info.storage_type()); + for &component_id in &component_ids { + // SAFE: component_id exists and is therefore valid + let component_info = components.get_info_unchecked(component_id); + storage_types.push(component_info.storage_type()); } let mut deduped = component_ids.clone(); diff --git a/crates/bevy_ecs/src/component/mod.rs b/crates/bevy_ecs/src/component.rs similarity index 82% rename from crates/bevy_ecs/src/component/mod.rs rename to crates/bevy_ecs/src/component.rs index ccb8a6f993e6d..6e71a1a7d9328 100644 --- a/crates/bevy_ecs/src/component/mod.rs +++ b/crates/bevy_ecs/src/component.rs @@ -1,7 +1,3 @@ -mod type_info; - -pub use type_info::*; - use crate::storage::SparseSetIndex; use std::{ alloc::Layout, @@ -56,15 +52,8 @@ impl Default for StorageType { #[derive(Debug)] pub struct ComponentInfo { - name: String, id: ComponentId, - type_id: Option, - // SAFETY: This must remain private. It must only be set to "true" if this component is - // actually Send + Sync - is_send_and_sync: bool, - layout: Layout, - drop: unsafe fn(*mut u8), - storage_type: StorageType, + descriptor: ComponentDescriptor, } impl ComponentInfo { @@ -75,44 +64,36 @@ impl ComponentInfo { #[inline] pub fn name(&self) -> &str { - &self.name + &self.descriptor.name } #[inline] pub fn type_id(&self) -> Option { - self.type_id + self.descriptor.type_id } #[inline] pub fn layout(&self) -> Layout { - self.layout + self.descriptor.layout } #[inline] pub fn drop(&self) -> unsafe fn(*mut u8) { - self.drop + self.descriptor.drop } #[inline] pub fn storage_type(&self) -> StorageType { - self.storage_type + self.descriptor.storage_type } #[inline] pub fn is_send_and_sync(&self) -> bool { - self.is_send_and_sync + self.descriptor.is_send_and_sync } fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self { - ComponentInfo { - id, - name: descriptor.name, - storage_type: descriptor.storage_type, - type_id: descriptor.type_id, - is_send_and_sync: descriptor.is_send_and_sync, - drop: descriptor.drop, - layout: descriptor.layout, - } + ComponentInfo { id, descriptor } } } @@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId { } } +#[derive(Debug)] pub struct ComponentDescriptor { name: String, storage_type: StorageType, @@ -154,6 +136,11 @@ pub struct ComponentDescriptor { } impl ComponentDescriptor { + // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value. + unsafe fn drop_ptr(x: *mut u8) { + x.cast::().drop_in_place() + } + pub fn new(storage_type: StorageType) -> Self { Self { name: std::any::type_name::().to_string(), @@ -161,7 +148,18 @@ impl ComponentDescriptor { is_send_and_sync: true, type_id: Some(TypeId::of::()), layout: Layout::new::(), - drop: TypeInfo::drop_ptr::, + drop: Self::drop_ptr::, + } + } + + fn new_non_send(storage_type: StorageType) -> Self { + Self { + name: std::any::type_name::().to_string(), + storage_type, + is_send_and_sync: false, + type_id: Some(TypeId::of::()), + layout: Layout::new::(), + drop: Self::drop_ptr::, } } @@ -181,19 +179,6 @@ impl ComponentDescriptor { } } -impl From for ComponentDescriptor { - fn from(type_info: TypeInfo) -> Self { - Self { - name: type_info.type_name().to_string(), - storage_type: StorageType::default(), - is_send_and_sync: type_info.is_send_and_sync(), - type_id: Some(type_info.type_id()), - drop: type_info.drop(), - layout: type_info.layout(), - } - } -} - #[derive(Debug, Default)] pub struct Components { components: Vec, @@ -231,7 +216,12 @@ impl Components { #[inline] pub fn get_or_insert_id(&mut self) -> ComponentId { - self.get_or_insert_with(TypeId::of::(), TypeInfo::of::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_with(TypeId::of::(), || { + ComponentDescriptor::new::(StorageType::default()) + }) + } } #[inline] @@ -279,42 +269,58 @@ impl Components { #[inline] pub fn get_or_insert_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_resource_with(TypeId::of::(), || { + ComponentDescriptor::new::(StorageType::default()) + }) + } } #[inline] pub fn get_or_insert_non_send_resource_id(&mut self) -> ComponentId { - self.get_or_insert_resource_with(TypeId::of::(), TypeInfo::of_non_send_and_sync::) + // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`] + unsafe { + self.get_or_insert_resource_with(TypeId::of::(), || { + ComponentDescriptor::new_non_send::(StorageType::default()) + }) + } } + /// # Safety + /// + /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] - fn get_or_insert_resource_with( + unsafe fn get_or_insert_resource_with( &mut self, type_id: TypeId, - func: impl FnOnce() -> TypeInfo, + func: impl FnOnce() -> ComponentDescriptor, ) -> ComponentId { let components = &mut self.components; let index = self.resource_indices.entry(type_id).or_insert_with(|| { - let type_info = func(); + let descriptor = func(); let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); + components.push(ComponentInfo::new(ComponentId(index), descriptor)); index }); ComponentId(*index) } + /// # Safety + /// + /// The [`ComponentDescriptor`] must match the [`TypeId`] #[inline] - pub(crate) fn get_or_insert_with( + pub(crate) unsafe fn get_or_insert_with( &mut self, type_id: TypeId, - func: impl FnOnce() -> TypeInfo, + func: impl FnOnce() -> ComponentDescriptor, ) -> ComponentId { let components = &mut self.components; let index = self.indices.entry(type_id).or_insert_with(|| { - let type_info = func(); + let descriptor = func(); let index = components.len(); - components.push(ComponentInfo::new(ComponentId(index), type_info.into())); + components.push(ComponentInfo::new(ComponentId(index), descriptor)); index }); diff --git a/crates/bevy_ecs/src/component/type_info.rs b/crates/bevy_ecs/src/component/type_info.rs deleted file mode 100644 index 58839632f438f..0000000000000 --- a/crates/bevy_ecs/src/component/type_info.rs +++ /dev/null @@ -1,63 +0,0 @@ -use std::{alloc::Layout, any::TypeId}; - -/// Metadata required to store a component. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TypeInfo { - type_id: TypeId, - layout: Layout, - drop: unsafe fn(*mut u8), - type_name: &'static str, - is_send_and_sync: bool, -} - -impl TypeInfo { - /// Metadata for `T`. - pub fn of() -> Self { - Self { - type_id: TypeId::of::(), - layout: Layout::new::(), - is_send_and_sync: true, - drop: Self::drop_ptr::, - type_name: core::any::type_name::(), - } - } - - pub fn of_non_send_and_sync() -> Self { - Self { - type_id: TypeId::of::(), - layout: Layout::new::(), - is_send_and_sync: false, - drop: Self::drop_ptr::, - type_name: core::any::type_name::(), - } - } - - #[inline] - pub fn type_id(&self) -> TypeId { - self.type_id - } - - #[inline] - pub fn layout(&self) -> Layout { - self.layout - } - - #[inline] - pub fn drop(&self) -> unsafe fn(*mut u8) { - self.drop - } - - #[inline] - pub fn is_send_and_sync(&self) -> bool { - self.is_send_and_sync - } - - #[inline] - pub fn type_name(&self) -> &'static str { - self.type_name - } - - pub(crate) unsafe fn drop_ptr(x: *mut u8) { - x.cast::().drop_in_place() - } -} diff --git a/crates/bevy_ecs/src/entity/mod.rs b/crates/bevy_ecs/src/entity/mod.rs index 7be98eac0e621..4f4681757d3cb 100644 --- a/crates/bevy_ecs/src/entity/mod.rs +++ b/crates/bevy_ecs/src/entity/mod.rs @@ -34,6 +34,11 @@ pub enum AllocAtWithoutReplacement { impl Entity { /// Creates a new entity reference with a generation of 0. + /// + /// # Note + /// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`Commands::spawn`]. + /// This method should generally only be used for sharing entities across apps, and only when they have a + /// scheme worked out to share an ID space (which doesn't happen by default). pub fn new(id: u32) -> Entity { Entity { id, generation: 0 } } @@ -168,6 +173,7 @@ pub struct Entities { /// Once `flush()` is done, `free_cursor` will equal `pending.len()`. pending: Vec, free_cursor: AtomicI64, + /// Stores the number of free entities for [`len`](Entities::len) len: u32, } @@ -318,7 +324,7 @@ impl Entities { AllocAtWithoutReplacement::DidNotExist } else { let current_meta = &mut self.meta[entity.id as usize]; - if current_meta.location.archetype_id == ArchetypeId::invalid() { + if current_meta.location.archetype_id == ArchetypeId::INVALID { AllocAtWithoutReplacement::DidNotExist } else if current_meta.generation == entity.generation { AllocAtWithoutReplacement::Exists(current_meta.location) @@ -364,12 +370,12 @@ impl Entities { } } + /// Returns true if the [`Entities`] contains [`entity`](Entity). + // This will return false for entities which have been freed, even if + // not reallocated since the generation is incremented in `free` pub fn contains(&self, entity: Entity) -> bool { - // Note that out-of-range IDs are considered to be "contained" because - // they must be reserved IDs that we haven't flushed yet. - self.meta - .get(entity.id as usize) - .map_or(true, |meta| meta.generation == entity.generation) + self.resolve_from_id(entity.id()) + .map_or(false, |e| e.generation() == entity.generation) } pub fn clear(&mut self) { @@ -384,7 +390,7 @@ impl Entities { if (entity.id as usize) < self.meta.len() { let meta = &self.meta[entity.id as usize]; if meta.generation != entity.generation - || meta.location.archetype_id == ArchetypeId::invalid() + || meta.location.archetype_id == ArchetypeId::INVALID { return None; } @@ -394,31 +400,23 @@ impl Entities { } } - /// Panics if the given id would represent an index outside of `meta`. - /// - /// # Safety + /// Get the [`Entity`] with a given id, if it exists in this [`Entities`] collection + /// Returns `None` if this [`Entity`] is outside of the range of currently reserved Entities /// - /// Must only be called for currently allocated `id`s. - pub unsafe fn resolve_unknown_gen(&self, id: u32) -> Entity { - let meta_len = self.meta.len(); - - if meta_len > id as usize { - let meta = &self.meta[id as usize]; - Entity { - generation: meta.generation, - id, - } + /// Note: This method may return [`Entities`](Entity) which are currently free + /// Note that [`contains`](Entities::contains) will correctly return false for freed + /// entities, since it checks the generation + pub fn resolve_from_id(&self, id: u32) -> Option { + let idu = id as usize; + if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) { + Some(Entity { generation, id }) } else { - // See if it's pending, but not yet flushed. + // `id` is outside of the meta list - check whether it is reserved but not yet flushed. let free_cursor = self.free_cursor.load(Ordering::Relaxed); - let num_pending = std::cmp::max(-free_cursor, 0) as usize; - - if meta_len + num_pending > id as usize { - // Pending entities will have generation 0. - Entity { generation: 0, id } - } else { - panic!("entity id is out of range"); - } + // If this entity was manually created, then free_cursor might be positive + // Returning None handles that case correctly + let num_pending = usize::try_from(-free_cursor).ok()?; + (idu < self.meta.len() + num_pending).then(|| Entity { generation: 0, id }) } } @@ -431,7 +429,7 @@ impl Entities { /// /// # Safety /// Flush _must_ set the entity location to the correct ArchetypeId for the given Entity - /// each time init is called. This _can_ be ArchetypeId::invalid(), provided the Entity has + /// each time init is called. This _can_ be ArchetypeId::INVALID, provided the Entity has /// not been assigned to an Archetype. pub unsafe fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) { let free_cursor = self.free_cursor.get_mut(); @@ -476,7 +474,7 @@ impl Entities { pub fn flush_as_invalid(&mut self) { unsafe { self.flush(|_entity, location| { - location.archetype_id = ArchetypeId::invalid(); + location.archetype_id = ArchetypeId::INVALID; }) } } @@ -502,8 +500,8 @@ impl EntityMeta { const EMPTY: EntityMeta = EntityMeta { generation: 0, location: EntityLocation { - archetype_id: ArchetypeId::invalid(), - index: usize::max_value(), // dummy value, to be filled in + archetype_id: ArchetypeId::INVALID, + index: usize::MAX, // dummy value, to be filled in }, }; } diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index c815703be6441..f088b27f1060a 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -68,7 +68,7 @@ enum State { /// [`Events::update`] exactly once per update/frame. /// /// [`Events::update_system`] is a system that does this, typically intialized automatically using -/// [`AppBuilder::add_event`]. [EventReader]s are expected to read events from this collection at +/// [`App::add_event`]. [EventReader]s are expected to read events from this collection at /// least once per loop/frame. /// Events will persist across a single frame boundary and so ordering of event producers and /// consumers is not critical (although poorly-planned ordering may cause accumulating lag). @@ -115,9 +115,9 @@ enum State { /// An alternative call pattern would be to call [Events::update] manually across frames to control /// when events are cleared. /// This complicates consumption and risks ever-expanding memory usage if not cleaned up, -/// but can be done by adding your event as a resource instead of using [`AppBuilder::add_event`]. +/// but can be done by adding your event as a resource instead of using [`App::add_event`]. /// -/// [`AppBuilder::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.AppBuilder.html#method.add_event +/// [`App::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event #[derive(Debug)] pub struct Events { events_a: Vec>, @@ -151,21 +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<'s, 'w, T: Component> { +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<'s, 'w, T: Component> { +pub struct EventWriter<'w, 's, T: Component> { events: ResMut<'w, Events>, - // TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes? #[system_param(ignore)] marker: PhantomData<&'s usize>, } -impl<'s, 'w, T: Component> EventWriter<'s, 'w, T> { +impl<'w, 's, T: Component> EventWriter<'w, 's, T> { pub fn send(&mut self, event: T) { self.events.send(event); } @@ -255,7 +254,7 @@ fn internal_event_reader<'a, T>( } } -impl<'s, 'w, T: Component> EventReader<'s, 'w, 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/lib.rs b/crates/bevy_ecs/src/lib.rs index 8a4cc5eac659c..c51925b710569 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -22,7 +22,7 @@ pub mod prelude { change_detection::DetectChanges, entity::Entity, event::{EventReader, EventWriter}, - query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, + query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without}, schedule::{ AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping, @@ -41,7 +41,7 @@ mod tests { use crate as bevy_ecs; use crate::{ bundle::Bundle, - component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo}, + component::{Component, ComponentDescriptor, ComponentId, StorageType}, entity::Entity, query::{ Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, @@ -60,7 +60,9 @@ mod tests { #[derive(Debug, PartialEq, Eq)] struct A(usize); + #[derive(Debug, PartialEq, Eq)] struct B(usize); + #[derive(Debug, PartialEq, Eq)] struct C; #[derive(Clone, Debug)] @@ -102,21 +104,26 @@ mod tests { #[test] fn bundle_derive() { + let mut world = World::new(); + #[derive(Bundle, PartialEq, Debug)] struct Foo { x: &'static str, y: i32, } - assert_eq!( - ::type_info(), - vec![TypeInfo::of::<&'static str>(), TypeInfo::of::(),] - ); - - let mut world = World::new(); world .register_component(ComponentDescriptor::new::(StorageType::SparseSet)) .unwrap(); + + assert_eq!( + ::component_ids(world.components_mut()), + vec![ + world.components_mut().get_or_insert_id::<&'static str>(), + world.components_mut().get_or_insert_id::(), + ] + ); + let e1 = world.spawn().insert_bundle(Foo { x: "abc", y: 123 }).id(); let e2 = world.spawn().insert_bundle(("def", 456, true)).id(); assert_eq!(*world.get::<&str>(e1).unwrap(), "abc"); @@ -146,12 +153,12 @@ mod tests { } assert_eq!( - ::type_info(), + ::component_ids(world.components_mut()), vec![ - TypeInfo::of::(), - TypeInfo::of::<&'static str>(), - TypeInfo::of::(), - TypeInfo::of::(), + world.components_mut().get_or_insert_id::(), + world.components_mut().get_or_insert_id::<&'static str>(), + world.components_mut().get_or_insert_id::(), + world.components_mut().get_or_insert_id::(), ] ); @@ -1393,4 +1400,89 @@ mod tests { "space between original entities and high entities is used for new entity ids" ); } + + #[test] + fn insert_or_spawn_batch() { + let mut world = World::default(); + let e0 = world.spawn().insert(A(0)).id(); + let e1 = Entity::new(1); + + let values = vec![(e0, (B(0), C)), (e1, (B(1), C))]; + + world.insert_or_spawn_batch(values).unwrap(); + + assert_eq!( + world.get::(e0), + Some(&A(0)), + "existing component was preserved" + ); + assert_eq!( + world.get::(e0), + Some(&B(0)), + "pre-existing entity received correct B component" + ); + assert_eq!( + world.get::(e1), + Some(&B(1)), + "new entity was spawned and received correct B component" + ); + assert_eq!( + world.get::(e0), + Some(&C), + "pre-existing entity received C component" + ); + assert_eq!( + world.get::(e1), + Some(&C), + "new entity was spawned and received C component" + ); + } + + #[test] + fn insert_or_spawn_batch_invalid() { + let mut world = World::default(); + let e0 = world.spawn().insert(A(0)).id(); + let e1 = Entity::new(1); + let e2 = world.spawn().id(); + let invalid_e2 = Entity { + generation: 1, + id: e2.id, + }; + + let values = vec![(e0, (B(0), C)), (e1, (B(1), C)), (invalid_e2, (B(2), C))]; + + let result = world.insert_or_spawn_batch(values); + + assert_eq!( + result, + Err(vec![invalid_e2]), + "e2 failed to be spawned or inserted into" + ); + + assert_eq!( + world.get::(e0), + Some(&A(0)), + "existing component was preserved" + ); + assert_eq!( + world.get::(e0), + Some(&B(0)), + "pre-existing entity received correct B component" + ); + assert_eq!( + world.get::(e1), + Some(&B(1)), + "new entity was spawned and received correct B component" + ); + assert_eq!( + world.get::(e0), + Some(&C), + "pre-existing entity received C component" + ); + assert_eq!( + world.get::(e1), + Some(&C), + "new entity was spawned and received C component" + ); + } } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 97bdda0462c45..9e20c208761e7 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -137,6 +137,7 @@ impl WorldQuery for Entity { } /// The [`Fetch`] of [`Entity`]. +#[derive(Clone)] pub struct EntityFetch { entities: *const Entity, } @@ -573,6 +574,7 @@ impl WorldQuery for Option { } /// The [`Fetch`] of `Option`. +#[derive(Clone)] pub struct OptionFetch { fetch: T, matches: bool, @@ -807,6 +809,21 @@ pub struct ChangeTrackersFetch { change_tick: u32, } +impl Clone for ChangeTrackersFetch { + fn clone(&self) -> Self { + Self { + storage_type: self.storage_type, + table_ticks: self.table_ticks, + entity_table_rows: self.entity_table_rows, + entities: self.entities, + sparse_set: self.sparse_set, + marker: self.marker, + last_change_tick: self.last_change_tick, + change_tick: self.change_tick, + } + } +} + /// SAFETY: access is read only unsafe impl ReadOnlyFetch for ChangeTrackersFetch {} diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 6d14d5160efe1..69dd298cd31e0 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -1,6 +1,5 @@ use crate::{ archetype::{Archetype, ArchetypeComponentId}, - bundle::Bundle, component::{Component, ComponentId, ComponentTicks, StorageType}, entity::Entity, query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, @@ -12,7 +11,7 @@ use std::{cell::UnsafeCell, marker::PhantomData, ptr}; /// Extension trait for [`Fetch`] containing methods used by query filters. /// This trait exists to allow "short circuit" behaviors for relevant query filter fetches. -pub trait FilterFetch: for<'world, 'state> Fetch<'world, 'state> { +pub trait FilterFetch: for<'w, 's> Fetch<'w, 's> { /// # Safety /// /// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range @@ -282,106 +281,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch { } } -pub struct WithBundle(PhantomData); - -impl WorldQuery for WithBundle { - type Fetch = WithBundleFetch; - type State = WithBundleState; -} - -pub struct WithBundleFetch { - is_dense: bool, - marker: PhantomData, -} - -pub struct WithBundleState { - component_ids: Vec, - is_dense: bool, - marker: PhantomData, -} - -// SAFETY: no component access or archetype component access -unsafe impl FetchState for WithBundleState { - fn init(world: &mut World) -> Self { - let bundle_info = world.bundles.init_info::(&mut world.components); - let components = &world.components; - Self { - component_ids: bundle_info.component_ids.clone(), - is_dense: !bundle_info.component_ids.iter().any(|id| unsafe { - components.get_info_unchecked(*id).storage_type() != StorageType::Table - }), - marker: PhantomData, - } - } - - #[inline] - fn update_component_access(&self, access: &mut FilteredAccess) { - for component_id in self.component_ids.iter().cloned() { - access.add_with(component_id); - } - } - - #[inline] - fn update_archetype_component_access( - &self, - _archetype: &Archetype, - _access: &mut Access, - ) { - } - - fn matches_archetype(&self, archetype: &Archetype) -> bool { - self.component_ids.iter().all(|id| archetype.contains(*id)) - } - - fn matches_table(&self, table: &Table) -> bool { - self.component_ids.iter().all(|id| table.has_column(*id)) - } -} - -impl<'w, 's, T: Bundle> Fetch<'w, 's> for WithBundleFetch { - type Item = bool; - type State = WithBundleState; - - unsafe fn init( - _world: &World, - state: &Self::State, - _last_change_tick: u32, - _change_tick: u32, - ) -> Self { - Self { - is_dense: state.is_dense, - marker: PhantomData, - } - } - - #[inline] - fn is_dense(&self) -> bool { - self.is_dense - } - - #[inline] - unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {} - - #[inline] - unsafe fn set_archetype( - &mut self, - _state: &Self::State, - _archetype: &Archetype, - _tables: &Tables, - ) { - } - - #[inline] - unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> bool { - true - } - - #[inline] - unsafe fn table_fetch(&mut self, _table_row: usize) -> bool { - true - } -} - /// A filter that tests if any of the given filters apply. /// /// This is useful for example if a system with multiple components in a query only wants to run diff --git a/crates/bevy_ecs/src/query/iter.rs b/crates/bevy_ecs/src/query/iter.rs index 4ad402ccfe7d8..7585e150878d8 100644 --- a/crates/bevy_ecs/src/query/iter.rs +++ b/crates/bevy_ecs/src/query/iter.rs @@ -4,7 +4,7 @@ use crate::{ storage::{TableId, Tables}, world::World, }; -use std::{marker::PhantomData, mem::MaybeUninit}; +use std::mem::MaybeUninit; /// An [`Iterator`] over query results of a [`Query`](crate::system::Query). /// @@ -217,7 +217,7 @@ where archetypes: &'w Archetypes, query_state: &'s QueryState, world: &'w World, - cursors: [QueryIterationCursor<'w, 's, Q, F>; K], + cursors: [QueryIterationCursor<'s, Q, F>; K], } impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<'w, 's, Q, F, K> @@ -239,7 +239,7 @@ where // There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit // TODO: use MaybeUninit::uninit_array if it stabilizes - let mut cursors: [MaybeUninit>; K] = + let mut cursors: [MaybeUninit>; K] = MaybeUninit::uninit().assume_init(); for (i, cursor) in cursors.iter_mut().enumerate() { match i { @@ -259,8 +259,8 @@ where } // TODO: use MaybeUninit::array_assume_init if it stabilizes - let cursors: [QueryIterationCursor<'w, 's, Q, F>; K] = - (&cursors as *const _ as *const [QueryIterationCursor<'w, 's, Q, F>; K]).read(); + let cursors: [QueryIterationCursor<'s, Q, F>; K] = + (&cursors as *const _ as *const [QueryIterationCursor<'s, Q, F>; K]).read(); QueryCombinationIter { world, @@ -277,9 +277,9 @@ where /// references to the same component, leading to unique reference aliasing. ///. /// It is always safe for shared access. - unsafe fn fetch_next_aliased_unchecked( + unsafe fn fetch_next_aliased_unchecked<'a>( &mut self, - ) -> Option<[>::Item; K]> + ) -> Option<[>::Item; K]> where Q::Fetch: Clone, F::Fetch: Clone, @@ -309,7 +309,7 @@ where } // TODO: use MaybeUninit::uninit_array if it stabilizes - let mut values: [MaybeUninit<>::Item>; K] = + let mut values: [MaybeUninit<>::Item>; K] = MaybeUninit::uninit().assume_init(); for (value, cursor) in values.iter_mut().zip(&mut self.cursors) { @@ -317,15 +317,15 @@ where } // TODO: use MaybeUninit::array_assume_init if it stabilizes - let values: [>::Item; K] = - (&values as *const _ as *const [>::Item; K]).read(); + let values: [>::Item; K] = + (&values as *const _ as *const [>::Item; K]).read(); Some(values) } /// Get next combination of queried components #[inline] - pub fn fetch_next(&mut self) -> Option<[>::Item; K]> + pub fn fetch_next(&mut self) -> Option<[>::Item; K]> where Q::Fetch: Clone, F::Fetch: Clone, @@ -400,7 +400,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> { } } -struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> { +struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> { table_id_iter: std::slice::Iter<'s, TableId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, fetch: Q::Fetch, @@ -408,10 +408,9 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> { current_len: usize, current_index: usize, is_dense: bool, - marker: PhantomData<&'w usize>, } -impl<'w, 's, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, F> +impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F> where Q::Fetch: Clone, F::Fetch: Clone, @@ -425,12 +424,11 @@ where current_len: self.current_len, current_index: self.current_index, is_dense: self.is_dense, - marker: PhantomData, } } } -impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> +impl<'s, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'s, Q, F> where F::Fetch: FilterFetch, { @@ -473,13 +471,12 @@ where archetype_id_iter: query_state.matched_archetype_ids.iter(), current_len: 0, current_index: 0, - marker: PhantomData, } } /// retrieve item returned from most recent `next` call again. #[inline] - unsafe fn peek_last(&mut self) -> Option<>::Item> { + unsafe fn peek_last<'w>(&mut self) -> Option<>::Item> { if self.current_index > 0 { if self.is_dense { Some(self.fetch.table_fetch(self.current_index - 1)) @@ -495,7 +492,7 @@ where // QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual // We can't currently reuse QueryIterationCursor in QueryIter for performance reasons. See #1763 for context. #[inline(always)] - unsafe fn next( + unsafe fn next<'w>( &mut self, tables: &'w Tables, archetypes: &'w Archetypes, diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index 3ab823ddaf614..9a5e5a36e8e5c 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -13,6 +13,7 @@ use bevy_tasks::TaskPool; use fixedbitset::FixedBitSet; use thiserror::Error; +/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter. pub struct QueryState where F::Fetch: FilterFetch, @@ -35,6 +36,7 @@ impl QueryState where F::Fetch: FilterFetch, { + /// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`. pub fn new(world: &mut World) -> Self { let fetch_state = ::init(world); let filter_state = ::init(world); @@ -64,10 +66,11 @@ where matched_archetypes: Default::default(), archetype_component_access: Default::default(), }; - state.update_archetypes(world); + state.validate_world_and_update_archetypes(world); state } + /// Checks if the query is empty for the given [`World`], where the last change and current tick are given. #[inline] pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool { // SAFE: the iterator is instantly consumed via `none_remaining` and the implementation of @@ -78,8 +81,17 @@ where } } - pub fn update_archetypes(&mut self, world: &World) { - self.validate_world(world); + /// Takes a query for the given [`World`], checks if the given world is the same as the query, and + /// generates new archetypes for the given world. + /// + /// # Panics + /// + /// Panics if the `world.id()` does not equal the current [`QueryState`] internal id. + pub fn validate_world_and_update_archetypes(&mut self, world: &World) { + if world.id() != self.world_id { + panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", + std::any::type_name::()); + } let archetypes = world.archetypes(); let new_generation = archetypes.generation(); let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); @@ -90,14 +102,7 @@ where } } - #[inline] - pub fn validate_world(&self, world: &World) { - if world.id() != self.world_id { - panic!("Attempted to use {} with a mismatched World. QueryStates can only be used with the World they were created from.", - std::any::type_name::()); - } - } - + /// Creates a new [`Archetype`]. pub fn new_archetype(&mut self, archetype: &Archetype) { if self.fetch_state.matches_archetype(archetype) && self.filter_state.matches_archetype(archetype) @@ -121,6 +126,9 @@ where } } + /// Gets the query result for the given [`World`] and [`Entity`]. + /// + /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries. #[inline] pub fn get<'w, 's>( &'s mut self, @@ -134,27 +142,7 @@ where unsafe { self.get_unchecked(world, entity) } } - #[inline] - pub fn get_manual<'w, 's>( - &'s self, - world: &'w World, - entity: Entity, - ) -> Result<>::Item, QueryEntityError> - where - Q::Fetch: ReadOnlyFetch, - { - self.validate_world(world); - // SAFETY: query is read only and world is validated - unsafe { - self.get_unchecked_manual( - world, - entity, - world.last_change_tick(), - world.read_change_tick(), - ) - } - } - + /// Gets the query result for the given [`World`] and [`Entity`]. #[inline] pub fn get_mut<'w, 's>( &'s mut self, @@ -165,6 +153,8 @@ where unsafe { self.get_unchecked(world, entity) } } + /// Gets the query result for the given [`World`] and [`Entity`]. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -175,7 +165,7 @@ where world: &'w World, entity: Entity, ) -> Result<>::Item, QueryEntityError> { - self.update_archetypes(world); + self.validate_world_and_update_archetypes(world); self.get_unchecked_manual( world, entity, @@ -184,7 +174,11 @@ where ) } + /// Gets the query result for the given [`World`] and [`Entity`], where the last change and + /// the current change tick are given. + /// /// # 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, 's>( @@ -219,6 +213,9 @@ where } } + /// Returns an [`Iterator`] over the query results for the given [`World`]. + /// + /// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries. #[inline] pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F> where @@ -228,24 +225,23 @@ where unsafe { self.iter_unchecked(world) } } - #[inline] - pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F> - where - Q::Fetch: ReadOnlyFetch, - { - self.validate_world(world); - // SAFETY: query is read only and world is validated - unsafe { - self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) - } - } - + /// Returns an [`Iterator`] over the query results for the given [`World`]. #[inline] pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> { // SAFETY: query has unique world access unsafe { self.iter_unchecked(world) } } + /// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition. + /// This can only be called for read-only queries. + /// + /// For permutations of size K of query returning N results, you will get: + /// - if K == N: one permutation of all query results + /// - if K < N: all possible K-sized combinations of query results, without repetition + /// - if K > N: empty set (no K-sized combinations exist) + /// + /// This can only be called for read-only queries, see [`Self::iter_combinations_mut`] for + /// write-queries. #[inline] pub fn iter_combinations<'w, 's, const K: usize>( &'s mut self, @@ -258,6 +254,13 @@ where unsafe { self.iter_combinations_unchecked(world) } } + /// Iterates over all possible combinations of `K` query results for the given [`World`] + /// without repetition. + /// + /// For permutations of size K of query returning N results, you will get: + /// - if K == N: one permutation of all query results + /// - if K < N: all possible K-sized combinations of query results, without repetition + /// - if K > N: empty set (no K-sized combinations exist) #[inline] pub fn iter_combinations_mut<'w, 's, const K: usize>( &'s mut self, @@ -267,6 +270,8 @@ where unsafe { self.iter_combinations_unchecked(world) } } + /// Returns an [`Iterator`] over the query results for the given [`World`]. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -276,10 +281,14 @@ where &'s mut self, world: &'w World, ) -> QueryIter<'w, 's, Q, F> { - self.update_archetypes(world); + self.validate_world_and_update_archetypes(world); self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) } + /// Returns an [`Iterator`] over all possible combinations of `K` query results for the + /// given [`World`] without repetition. + /// This can only be called for read-only queries. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -289,7 +298,7 @@ where &'s mut self, world: &'w World, ) -> QueryCombinationIter<'w, 's, Q, F, K> { - self.update_archetypes(world); + self.validate_world_and_update_archetypes(world); self.iter_combinations_unchecked_manual( world, world.last_change_tick(), @@ -297,7 +306,11 @@ where ) } + /// Returns an [`Iterator`] for the given [`World`], where the last change and + /// the current change tick are given. + /// /// # Safety + /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` @@ -312,7 +325,12 @@ where QueryIter::new(world, self, last_change_tick, change_tick) } + /// Returns an [`Iterator`] over all possible combinations of `K` query results for the + /// given [`World`] without repetition. + /// This can only be called for read-only queries. + /// /// # Safety + /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries /// have unique access to the components they query. /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` @@ -327,6 +345,10 @@ where QueryCombinationIter::new(world, self, last_change_tick, change_tick) } + /// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent + /// iter() method, but cannot be chained like a normal [`Iterator`]. + /// + /// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries. #[inline] pub fn for_each<'w, 's>( &'s mut self, @@ -341,6 +363,8 @@ where } } + /// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent + /// iter_mut() method, but cannot be chained like a normal [`Iterator`]. #[inline] pub fn for_each_mut<'w, 's>( &'s mut self, @@ -353,6 +377,11 @@ where } } + /// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent + /// iter() method, but cannot be chained like a normal [`Iterator`]. + /// + /// This can only be called for read-only queries. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -363,7 +392,7 @@ where world: &'w World, func: impl FnMut(>::Item), ) { - self.update_archetypes(world); + self.validate_world_and_update_archetypes(world); self.for_each_unchecked_manual( world, func, @@ -372,6 +401,10 @@ where ); } + /// Runs `func` on each query result in parallel using the given `task_pool`. + /// + /// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for + /// write-queries. #[inline] pub fn par_for_each<'w, 's>( &'s mut self, @@ -388,6 +421,7 @@ where } } + /// Runs `func` on each query result in parallel using the given `task_pool`. #[inline] pub fn par_for_each_mut<'w, 's>( &'s mut self, @@ -402,6 +436,10 @@ where } } + /// Runs `func` on each query result in parallel using the given `task_pool`. + /// + /// This can only be called for read-only queries. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -414,7 +452,7 @@ where batch_size: usize, func: impl Fn(>::Item) + Send + Sync + Clone, ) { - self.update_archetypes(world); + self.validate_world_and_update_archetypes(world); self.par_for_each_unchecked_manual( world, task_pool, @@ -425,6 +463,10 @@ where ); } + /// Runs `func` on each query result for the given [`World`], where the last change and + /// the current change tick are given. This is faster than the equivalent + /// iter() method, but cannot be chained like a normal [`Iterator`]. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries @@ -477,6 +519,10 @@ where } } + /// Runs `func` on each query result in parallel for the given [`World`], where the last change and + /// the current change tick are given. This is faster than the equivalent + /// iter() method, but cannot be chained like a normal [`Iterator`]. + /// /// # Safety /// /// This does not check for mutable query correctness. To be safe, make sure mutable queries diff --git a/crates/bevy_ecs/src/reflect.rs b/crates/bevy_ecs/src/reflect.rs index d1f4897b00fd7..f798b753f94f5 100644 --- a/crates/bevy_ecs/src/reflect.rs +++ b/crates/bevy_ecs/src/reflect.rs @@ -10,6 +10,7 @@ use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize}; pub struct ReflectComponent { add_component: fn(&mut World, Entity, &dyn Reflect), apply_component: fn(&mut World, Entity, &dyn Reflect), + remove_component: fn(&mut World, Entity), reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>, reflect_component_mut: unsafe fn(&World, Entity) -> Option, copy_component: fn(&World, &mut World, Entity, Entity), @@ -24,6 +25,10 @@ impl ReflectComponent { (self.apply_component)(world, entity, component); } + pub fn remove_component(&self, world: &mut World, entity: Entity) { + (self.remove_component)(world, entity); + } + pub fn reflect_component<'a>( &self, world: &'a World, @@ -83,6 +88,9 @@ impl FromType for ReflectComponent { let mut component = world.get_mut::(entity).unwrap(); component.apply(reflected_component); }, + remove_component: |world, entity| { + world.entity_mut(entity).remove::(); + }, copy_component: |source_world, destination_world, source_entity, destination_entity| { let source_component = source_world.get::(source_entity).unwrap(); let mut destination_component = C::from_world(destination_world); diff --git a/crates/bevy_ecs/src/schedule/executor_parallel.rs b/crates/bevy_ecs/src/schedule/executor_parallel.rs index 1a4baf38fbb34..42cd1318980c6 100644 --- a/crates/bevy_ecs/src/schedule/executor_parallel.rs +++ b/crates/bevy_ecs/src/schedule/executor_parallel.rs @@ -316,7 +316,7 @@ mod tests { use super::SchedulingEvent::{self, *}; use crate::{ schedule::{SingleThreadedExecutor, Stage, SystemStage}, - system::{IntoSystem, NonSend, Query, Res, ResMut}, + system::{NonSend, Query, Res, ResMut}, world::World, }; use async_channel::Receiver; @@ -338,9 +338,9 @@ mod tests { let mut world = World::new(); fn wants_for_nothing() {} let mut stage = SystemStage::parallel() - .with_system(wants_for_nothing.system()) - .with_system(wants_for_nothing.system()) - .with_system(wants_for_nothing.system()); + .with_system(wants_for_nothing) + .with_system(wants_for_nothing) + .with_system(wants_for_nothing); stage.run(&mut world); stage.run(&mut world); assert_eq!( @@ -356,24 +356,24 @@ mod tests { fn wants_mut(_: ResMut) {} fn wants_ref(_: Res) {} let mut stage = SystemStage::parallel() - .with_system(wants_mut.system()) - .with_system(wants_mut.system()); + .with_system(wants_mut) + .with_system(wants_mut); stage.run(&mut world); assert_eq!( receive_events(&world), vec![StartedSystems(1), StartedSystems(1),] ); let mut stage = SystemStage::parallel() - .with_system(wants_mut.system()) - .with_system(wants_ref.system()); + .with_system(wants_mut) + .with_system(wants_ref); stage.run(&mut world); assert_eq!( receive_events(&world), vec![StartedSystems(1), StartedSystems(1),] ); let mut stage = SystemStage::parallel() - .with_system(wants_ref.system()) - .with_system(wants_ref.system()); + .with_system(wants_ref) + .with_system(wants_ref); stage.run(&mut world); assert_eq!(receive_events(&world), vec![StartedSystems(2),]); } @@ -385,24 +385,24 @@ mod tests { fn wants_mut(_: Query<&mut usize>) {} fn wants_ref(_: Query<&usize>) {} let mut stage = SystemStage::parallel() - .with_system(wants_mut.system()) - .with_system(wants_mut.system()); + .with_system(wants_mut) + .with_system(wants_mut); stage.run(&mut world); assert_eq!( receive_events(&world), vec![StartedSystems(1), StartedSystems(1),] ); let mut stage = SystemStage::parallel() - .with_system(wants_mut.system()) - .with_system(wants_ref.system()); + .with_system(wants_mut) + .with_system(wants_ref); stage.run(&mut world); assert_eq!( receive_events(&world), vec![StartedSystems(1), StartedSystems(1),] ); let mut stage = SystemStage::parallel() - .with_system(wants_ref.system()) - .with_system(wants_ref.system()); + .with_system(wants_ref) + .with_system(wants_ref); stage.run(&mut world); assert_eq!(receive_events(&world), vec![StartedSystems(2),]); let mut world = World::new(); @@ -410,8 +410,8 @@ mod tests { fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {} fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {} let mut stage = SystemStage::parallel() - .with_system(wants_mut_usize.system()) - .with_system(wants_mut_u32.system()); + .with_system(wants_mut_usize) + .with_system(wants_mut_u32); stage.run(&mut world); assert_eq!(receive_events(&world), vec![StartedSystems(2),]); } @@ -426,12 +426,12 @@ mod tests { } fn empty() {} let mut stage = SystemStage::parallel() - .with_system(non_send.system()) - .with_system(non_send.system()) - .with_system(empty.system()) - .with_system(empty.system()) - .with_system(non_send.system()) - .with_system(non_send.system()); + .with_system(non_send) + .with_system(non_send) + .with_system(empty) + .with_system(empty) + .with_system(non_send) + .with_system(non_send); stage.run(&mut world); assert_eq!( receive_events(&world), diff --git a/crates/bevy_ecs/src/schedule/label.rs b/crates/bevy_ecs/src/schedule/label.rs index ed9398426e4f8..a7f3538741242 100644 --- a/crates/bevy_ecs/src/schedule/label.rs +++ b/crates/bevy_ecs/src/schedule/label.rs @@ -1,15 +1,115 @@ pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel}; -use bevy_utils::define_label; +use std::{ + any::Any, + borrow::Cow, + fmt::Debug, + hash::{Hash, Hasher}, +}; -define_label!(StageLabel); +pub trait DynEq: Any { + fn as_any(&self) -> &dyn Any; + + fn dyn_eq(&self, other: &dyn DynEq) -> bool; +} + +impl DynEq for T +where + T: Any + Eq, +{ + fn as_any(&self) -> &dyn Any { + self + } + + fn dyn_eq(&self, other: &dyn DynEq) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + return self == other; + } + false + } +} + +pub trait DynHash: DynEq { + fn as_dyn_eq(&self) -> &dyn DynEq; + + fn dyn_hash(&self, state: &mut dyn Hasher); +} + +impl DynHash for T +where + T: DynEq + Hash, +{ + fn as_dyn_eq(&self) -> &dyn DynEq { + self + } + + fn dyn_hash(&self, mut state: &mut dyn Hasher) { + T::hash(self, &mut state); + self.type_id().hash(&mut state); + } +} + +pub trait StageLabel: DynHash + Debug + Send + Sync + 'static { + #[doc(hidden)] + fn dyn_clone(&self) -> Box; +} pub(crate) type BoxedStageLabel = Box; -define_label!(SystemLabel); +pub trait SystemLabel: DynHash + Debug + Send + Sync + 'static { + #[doc(hidden)] + fn dyn_clone(&self) -> Box; +} pub(crate) type BoxedSystemLabel = Box; -define_label!(AmbiguitySetLabel); +pub trait AmbiguitySetLabel: DynHash + Debug + Send + Sync + 'static { + #[doc(hidden)] + fn dyn_clone(&self) -> Box; +} pub(crate) type BoxedAmbiguitySetLabel = Box; -define_label!(RunCriteriaLabel); +pub trait RunCriteriaLabel: DynHash + Debug + Send + Sync + 'static { + #[doc(hidden)] + fn dyn_clone(&self) -> Box; +} pub(crate) type BoxedRunCriteriaLabel = Box; + +macro_rules! impl_label { + ($trait_name:ident) => { + impl PartialEq for dyn $trait_name { + fn eq(&self, other: &Self) -> bool { + self.dyn_eq(other.as_dyn_eq()) + } + } + + impl Eq for dyn $trait_name {} + + impl Hash for dyn $trait_name { + fn hash(&self, state: &mut H) { + self.dyn_hash(state); + } + } + + impl Clone for Box { + fn clone(&self) -> Self { + self.dyn_clone() + } + } + + impl $trait_name for Cow<'static, str> { + fn dyn_clone(&self) -> Box { + Box::new(self.clone()) + } + } + + impl $trait_name for &'static str { + fn dyn_clone(&self) -> Box { + Box::new(<&str>::clone(self)) + } + } + }; +} + +impl_label!(StageLabel); +impl_label!(SystemLabel); +impl_label!(AmbiguitySetLabel); +impl_label!(RunCriteriaLabel); diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 67f21025cf0bf..a1c386446c5d0 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -22,10 +22,7 @@ pub use system_set::*; use std::fmt::Debug; -use crate::{ - system::{IntoSystem, System}, - world::World, -}; +use crate::{system::System, world::World}; use bevy_utils::HashMap; #[derive(Default)] @@ -79,7 +76,7 @@ impl Schedule { &mut self, system: S, ) -> &mut Self { - self.run_criteria.set(Box::new(system.system())); + self.run_criteria.set(Box::new(system)); self } diff --git a/crates/bevy_ecs/src/schedule/run_criteria.rs b/crates/bevy_ecs/src/schedule/run_criteria.rs index 7e3da8a5f3b3f..56c43452d3e60 100644 --- a/crates/bevy_ecs/src/schedule/run_criteria.rs +++ b/crates/bevy_ecs/src/schedule/run_criteria.rs @@ -61,12 +61,12 @@ impl Default for BoxedRunCriteria { } impl BoxedRunCriteria { - pub fn set(&mut self, criteria_system: BoxedSystem<(), ShouldRun>) { + pub(crate) fn set(&mut self, criteria_system: BoxedSystem<(), ShouldRun>) { self.criteria_system = Some(criteria_system); self.initialized = false; } - pub fn should_run(&mut self, world: &mut World) -> ShouldRun { + pub(crate) fn should_run(&mut self, world: &mut World) -> ShouldRun { if let Some(ref mut run_criteria) = self.criteria_system { if !self.initialized { run_criteria.initialize(world); @@ -99,16 +99,16 @@ pub(crate) enum RunCriteriaInner { } pub(crate) struct RunCriteriaContainer { - pub should_run: ShouldRun, - pub inner: RunCriteriaInner, - pub label: Option, - pub before: Vec, - pub after: Vec, + pub(crate) should_run: ShouldRun, + pub(crate) inner: RunCriteriaInner, + pub(crate) label: Option, + pub(crate) before: Vec, + pub(crate) after: Vec, archetype_generation: ArchetypeGeneration, } impl RunCriteriaContainer { - pub fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self { + pub(crate) fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self { Self { should_run: ShouldRun::Yes, inner: match descriptor.system { @@ -122,21 +122,21 @@ impl RunCriteriaContainer { } } - pub fn name(&self) -> Cow<'static, str> { + pub(crate) fn name(&self) -> Cow<'static, str> { match &self.inner { RunCriteriaInner::Single(system) => system.name(), RunCriteriaInner::Piped { system, .. } => system.name(), } } - pub fn initialize(&mut self, world: &mut World) { + pub(crate) fn initialize(&mut self, world: &mut World) { match &mut self.inner { RunCriteriaInner::Single(system) => system.initialize(world), RunCriteriaInner::Piped { system, .. } => system.initialize(world), } } - pub fn update_archetypes(&mut self, world: &World) { + pub(crate) fn update_archetypes(&mut self, world: &World) { let archetypes = world.archetypes(); let new_generation = archetypes.generation(); let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 10980ccd60e22..9d5dda4bdf385 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -1,5 +1,6 @@ use crate::{ component::ComponentId, + prelude::IntoSystem, schedule::{ graph_utils::{self, DependencyGraphError}, BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy, @@ -8,7 +9,6 @@ use crate::{ RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun, SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet, }, - system::System, world::{World, WorldId}, }; use bevy_utils::{tracing::info, HashMap, HashSet}; @@ -26,7 +26,7 @@ pub trait Stage: Downcast + Send + Sync { impl_downcast!(Stage); -/// When this resource is present in the `AppBuilder`'s `Resources`, +/// When this resource is present in the `App`'s `Resources`, /// each `SystemStage` will log a report containing /// pairs of systems with ambiguous execution order. /// @@ -83,7 +83,6 @@ pub struct SystemStage { uninitialized_parallel: Vec, /// Saves the value of the World change_tick during the last tick check last_tick_check: u32, - apply_buffers: bool, } impl SystemStage { @@ -105,7 +104,6 @@ impl SystemStage { uninitialized_before_commands: vec![], uninitialized_at_end: vec![], last_tick_check: Default::default(), - apply_buffers: true, } } @@ -205,16 +203,6 @@ impl SystemStage { } } - pub fn apply_buffers(&mut self, world: &mut World) { - for container in self.parallel.iter_mut() { - container.system_mut().apply_buffers(world); - } - } - - pub fn set_apply_buffers(&mut self, apply_buffers: bool) { - self.apply_buffers = apply_buffers; - } - /// Topologically sorted parallel systems. /// /// Note that systems won't be fully-formed until the stage has been run at least once. @@ -301,16 +289,19 @@ impl SystemStage { self } - pub fn with_run_criteria>(mut self, system: S) -> Self { - self.set_run_criteria(system); + pub fn with_run_criteria>( + mut self, + system: S, + ) -> Self { + self.set_run_criteria(system.system()); self } - pub fn set_run_criteria>( + pub fn set_run_criteria>( &mut self, system: S, ) -> &mut Self { - self.stage_run_criteria.set(Box::new(system)); + self.stage_run_criteria.set(Box::new(system.system())); self } @@ -840,11 +831,9 @@ impl Stage for SystemStage { } // Apply parallel systems' buffers. - if self.apply_buffers { - for container in &mut self.parallel { - if container.should_run { - container.system_mut().apply_buffers(world); - } + for container in &mut self.parallel { + if container.should_run { + container.system_mut().apply_buffers(world); } } @@ -919,14 +908,8 @@ mod tests { move |world| world.get_resource_mut::>().unwrap().push(tag) } - // This is silly. https://github.com/bevyengine/bevy/issues/1029 - macro_rules! make_parallel { - ($tag:expr) => {{ - fn parallel(mut resource: ResMut>) { - resource.push($tag) - } - parallel - }}; + fn make_parallel(tag: usize) -> impl FnMut(ResMut>) { + move |mut resource: ResMut>| resource.push(tag) } fn every_other_time(mut has_ran: Local) -> ShouldRun { @@ -944,7 +927,7 @@ mod tests { world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() .with_system(make_exclusive(0).exclusive_system().at_start()) - .with_system(make_parallel!(1).system()) + .with_system(make_parallel(1)) .with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(3).exclusive_system().at_end()); stage.run(&mut world); @@ -963,7 +946,7 @@ mod tests { let mut stage = SystemStage::parallel() .with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(3).exclusive_system().at_end()) - .with_system(make_parallel!(1).system()) + .with_system(make_parallel(1)) .with_system(make_exclusive(0).exclusive_system().at_start()); stage.run(&mut world); assert_eq!( @@ -979,10 +962,10 @@ mod tests { world.get_resource_mut::>().unwrap().clear(); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(2).exclusive_system().before_commands()) - .with_system(make_parallel!(3).exclusive_system().at_end()) - .with_system(make_parallel!(1).system()) - .with_system(make_parallel!(0).exclusive_system().at_start()); + .with_system(make_parallel(2).exclusive_system().before_commands()) + .with_system(make_parallel(3).exclusive_system().at_end()) + .with_system(make_parallel(1)) + .with_system(make_parallel(0).exclusive_system().at_start()); stage.run(&mut world); assert_eq!( *world.get_resource::>().unwrap(), @@ -1239,9 +1222,9 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(1).system().after("0").label("1")) - .with_system(make_parallel!(2).system().after("1")) - .with_system(make_parallel!(0).system().label("0")); + .with_system(make_parallel(1).after("0").label("1")) + .with_system(make_parallel(2).after("1")) + .with_system(make_parallel(0).label("0")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1256,9 +1239,9 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(1).system().label("1").before("2")) - .with_system(make_parallel!(2).system().label("2")) - .with_system(make_parallel!(0).system().before("1")); + .with_system(make_parallel(1).label("1").before("2")) + .with_system(make_parallel(2).label("2")) + .with_system(make_parallel(0).before("1")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1273,11 +1256,11 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(2).system().label("2")) - .with_system(make_parallel!(1).system().after("0").before("2")) - .with_system(make_parallel!(0).system().label("0")) - .with_system(make_parallel!(4).system().label("4")) - .with_system(make_parallel!(3).system().after("2").before("4")); + .with_system(make_parallel(2).label("2")) + .with_system(make_parallel(1).after("0").before("2")) + .with_system(make_parallel(0).label("0")) + .with_system(make_parallel(4).label("4")) + .with_system(make_parallel(3).after("2").before("4")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1292,9 +1275,9 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(1).system().label("first").after("0")) - .with_system(make_parallel!(2).system().after("first")) - .with_system(make_parallel!(0).system().label("first").label("0")); + .with_system(make_parallel(1).label("first").after("0")) + .with_system(make_parallel(2).after("first")) + .with_system(make_parallel(0).label("first").label("0")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1305,11 +1288,11 @@ mod tests { world.get_resource_mut::>().unwrap().clear(); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(2).system().after("01").label("2")) - .with_system(make_parallel!(1).system().label("01").after("0")) - .with_system(make_parallel!(0).system().label("01").label("0")) - .with_system(make_parallel!(4).system().label("4")) - .with_system(make_parallel!(3).system().after("2").before("4")); + .with_system(make_parallel(2).after("01").label("2")) + .with_system(make_parallel(1).label("01").after("0")) + .with_system(make_parallel(0).label("01").label("0")) + .with_system(make_parallel(4).label("4")) + .with_system(make_parallel(3).after("2").before("4")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1320,17 +1303,11 @@ mod tests { world.get_resource_mut::>().unwrap().clear(); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(2).system().label("234").label("2")) - .with_system(make_parallel!(1).system().before("234").after("0")) - .with_system(make_parallel!(0).system().label("0")) - .with_system(make_parallel!(4).system().label("234").label("4")) - .with_system( - make_parallel!(3) - .system() - .label("234") - .after("2") - .before("4"), - ); + .with_system(make_parallel(2).label("234").label("2")) + .with_system(make_parallel(1).before("234").after("0")) + .with_system(make_parallel(0).label("0")) + .with_system(make_parallel(4).label("234").label("4")) + .with_system(make_parallel(3).label("234").after("2").before("4")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1346,24 +1323,22 @@ mod tests { world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() .with_system( - make_parallel!(2) - .system() + make_parallel(2) .label("2") .after("1") .before("3") .before("3"), ) .with_system( - make_parallel!(1) - .system() + make_parallel(1) .label("1") .after("0") .after("0") .before("2"), ) - .with_system(make_parallel!(0).system().label("0").before("1")) - .with_system(make_parallel!(4).system().label("4").after("3")) - .with_system(make_parallel!(3).system().label("3").after("2").before("4")); + .with_system(make_parallel(0).label("0").before("1")) + .with_system(make_parallel(4).label("4").after("3")) + .with_system(make_parallel(3).label("3").after("2").before("4")); stage.run(&mut world); for container in stage.parallel.iter() { assert!(container.dependencies().len() <= 1); @@ -1381,14 +1356,14 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(2).system().label("2")) + .with_system(make_parallel(2).label("2")) .with_system_set( SystemSet::new() - .with_system(make_parallel!(0).system().label("0")) - .with_system(make_parallel!(4).system().label("4")) - .with_system(make_parallel!(3).system().after("2").before("4")), + .with_system(make_parallel(0).label("0")) + .with_system(make_parallel(4).label("4")) + .with_system(make_parallel(3).after("2").before("4")), ) - .with_system(make_parallel!(1).system().after("0").before("2")); + .with_system(make_parallel(1).after("0").before("2")); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.run(&mut world); @@ -1405,12 +1380,11 @@ mod tests { world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() .with_system( - make_parallel!(0) - .system() + make_parallel(0) .label("0") .with_run_criteria(every_other_time), ) - .with_system(make_parallel!(1).system().after("0")); + .with_system(make_parallel(1).after("0")); stage.run(&mut world); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); @@ -1423,13 +1397,13 @@ mod tests { world.get_resource_mut::>().unwrap().clear(); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(0).system().before("1")) + .with_system(make_parallel(0).before("1")) .with_system_set( SystemSet::new() - .with_run_criteria(every_other_time.system()) - .with_system(make_parallel!(1).system().label("1")), + .with_run_criteria(every_other_time) + .with_system(make_parallel(1).label("1")), ) - .with_system(make_parallel!(2).system().after("1")); + .with_system(make_parallel(2).after("1")); stage.run(&mut world); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); @@ -1444,21 +1418,19 @@ mod tests { world.get_resource_mut::>().unwrap().clear(); let mut stage = SystemStage::parallel() .with_system_run_criteria(every_other_time.label("every other time")) - .with_system(make_parallel!(0).system().before("1")) + .with_system(make_parallel(0).before("1")) .with_system( - make_parallel!(1) - .system() + make_parallel(1) .label("1") .with_run_criteria("every other time"), ) .with_system( - make_parallel!(2) - .system() + make_parallel(2) .label("2") .after("1") .with_run_criteria("every other time"), ) - .with_system(make_parallel!(3).system().after("2")); + .with_system(make_parallel(3).after("2")); stage.run(&mut world); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); @@ -1480,34 +1452,26 @@ mod tests { } } let mut stage = SystemStage::parallel() - .with_system(make_parallel!(0).system().label("0")) + .with_system(make_parallel(0).label("0")) .with_system( - make_parallel!(1) - .system() + make_parallel(1) .label("1") .after("0") - .with_run_criteria(every_other_time.system().label("every other time")), + .with_run_criteria(every_other_time.label("every other time")), ) .with_system( - make_parallel!(2) - .system() + make_parallel(2) .label("2") .after("1") .with_run_criteria(RunCriteria::pipe("every other time", eot_piped.system())), ) .with_system( - make_parallel!(3) - .system() + make_parallel(3) .label("3") .after("2") .with_run_criteria("every other time".pipe(eot_piped.system()).label("piped")), ) - .with_system( - make_parallel!(4) - .system() - .after("3") - .with_run_criteria("piped"), - ); + .with_system(make_parallel(4).after("3").with_run_criteria("piped")); for _ in 0..4 { stage.run(&mut world); } @@ -1523,27 +1487,16 @@ mod tests { // Discarding extra criteria with matching labels. world.get_resource_mut::>().unwrap().clear(); - let mut stage = SystemStage::parallel() - .with_system(make_parallel!(0).system().before("1")) - .with_system( - make_parallel!(1).system().label("1").with_run_criteria( - every_other_time - .system() - .label_discard_if_duplicate("every other time"), - ), - ) - .with_system( - make_parallel!(2) - .system() - .label("2") - .after("1") - .with_run_criteria( - every_other_time - .system() - .label_discard_if_duplicate("every other time"), - ), - ) - .with_system(make_parallel!(3).system().after("2")); + let mut stage = + SystemStage::parallel() + .with_system(make_parallel(0).before("1")) + .with_system(make_parallel(1).label("1").with_run_criteria( + every_other_time.label_discard_if_duplicate("every other time"), + )) + .with_system(make_parallel(2).label("2").after("1").with_run_criteria( + every_other_time.label_discard_if_duplicate("every other time"), + )) + .with_system(make_parallel(3).after("2")); stage.run(&mut world); stage.run(&mut world); stage.set_executor(Box::new(SingleThreadedExecutor::default())); @@ -1561,8 +1514,8 @@ mod tests { fn duplicate_run_criteria_label_panic() { let mut world = World::new(); let mut stage = SystemStage::parallel() - .with_system_run_criteria(every_other_time.system().label("every other time")) - .with_system_run_criteria(every_other_time.system().label("every other time")); + .with_system_run_criteria(every_other_time.label("every other time")) + .with_system_run_criteria(every_other_time.label("every other time")); stage.run(&mut world); } @@ -1571,8 +1524,7 @@ mod tests { fn parallel_cycle_1() { let mut world = World::new(); world.insert_resource(Vec::::new()); - let mut stage = - SystemStage::parallel().with_system(make_parallel!(0).system().label("0").after("0")); + let mut stage = SystemStage::parallel().with_system(make_parallel(0).label("0").after("0")); stage.run(&mut world); } @@ -1582,8 +1534,8 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(0).system().label("0").after("1")) - .with_system(make_parallel!(1).system().label("1").after("0")); + .with_system(make_parallel(0).label("0").after("1")) + .with_system(make_parallel(1).label("1").after("0")); stage.run(&mut world); } @@ -1594,9 +1546,9 @@ mod tests { world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(make_parallel!(0).system().label("0")) - .with_system(make_parallel!(1).system().after("0").before("2")) - .with_system(make_parallel!(2).system().label("2").before("0")); + .with_system(make_parallel(0).label("0")) + .with_system(make_parallel(1).after("0").before("2")) + .with_system(make_parallel(2).label("2").before("0")); stage.run(&mut world); } @@ -1625,21 +1577,21 @@ mod tests { let mut world = World::new(); let mut stage = SystemStage::parallel() - .with_system(empty.system().label("0")) - .with_system(empty.system().label("1").after("0")) - .with_system(empty.system().label("2")) - .with_system(empty.system().label("3").after("2").before("4")) - .with_system(empty.system().label("4")); + .with_system(empty.label("0")) + .with_system(empty.label("1").after("0")) + .with_system(empty.label("2")) + .with_system(empty.label("3").after("2").before("4")) + .with_system(empty.label("4")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); assert_eq!(find_ambiguities(&stage.parallel).len(), 0); let mut stage = SystemStage::parallel() - .with_system(empty.system().label("0")) - .with_system(component.system().label("1").after("0")) - .with_system(empty.system().label("2")) - .with_system(empty.system().label("3").after("2").before("4")) - .with_system(component.system().label("4")); + .with_system(empty.label("0")) + .with_system(component.label("1").after("0")) + .with_system(empty.label("2")) + .with_system(empty.label("3").after("2").before("4")) + .with_system(component.label("4")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1650,11 +1602,11 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(empty.system().label("0")) - .with_system(resource.system().label("1").after("0")) - .with_system(empty.system().label("2")) - .with_system(empty.system().label("3").after("2").before("4")) - .with_system(resource.system().label("4")); + .with_system(empty.label("0")) + .with_system(resource.label("1").after("0")) + .with_system(empty.label("2")) + .with_system(empty.label("3").after("2").before("4")) + .with_system(resource.label("4")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1665,21 +1617,21 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(empty.system().label("0")) - .with_system(resource.system().label("1").after("0")) - .with_system(empty.system().label("2")) - .with_system(empty.system().label("3").after("2").before("4")) - .with_system(component.system().label("4")); + .with_system(empty.label("0")) + .with_system(resource.label("1").after("0")) + .with_system(empty.label("2")) + .with_system(empty.label("3").after("2").before("4")) + .with_system(component.label("4")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); assert_eq!(find_ambiguities(&stage.parallel).len(), 0); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0")) - .with_system(resource.system().label("1").after("0")) - .with_system(empty.system().label("2")) - .with_system(component.system().label("3").after("2").before("4")) - .with_system(resource.system().label("4")); + .with_system(component.label("0")) + .with_system(resource.label("1").after("0")) + .with_system(empty.label("2")) + .with_system(component.label("3").after("2").before("4")) + .with_system(resource.label("4")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1694,17 +1646,11 @@ mod tests { assert_eq!(ambiguities.len(), 2); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0")) - .with_system( - resource - .system() - .label("1") - .after("0") - .in_ambiguity_set("a"), - ) - .with_system(empty.system().label("2")) - .with_system(component.system().label("3").after("2").before("4")) - .with_system(resource.system().label("4").in_ambiguity_set("a")); + .with_system(component.label("0")) + .with_system(resource.label("1").after("0").in_ambiguity_set("a")) + .with_system(empty.label("2")) + .with_system(component.label("3").after("2").before("4")) + .with_system(resource.label("4").in_ambiguity_set("a")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1715,9 +1661,9 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0").before("2")) - .with_system(component.system().label("1").before("2")) - .with_system(component.system().label("2")); + .with_system(component.label("0").before("2")) + .with_system(component.label("1").before("2")) + .with_system(component.label("2")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1728,9 +1674,9 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0")) - .with_system(component.system().label("1").after("0")) - .with_system(component.system().label("2").after("0")); + .with_system(component.label("0")) + .with_system(component.label("1").after("0")) + .with_system(component.label("2").after("0")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1741,10 +1687,10 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0").before("1").before("2")) - .with_system(component.system().label("1")) - .with_system(component.system().label("2")) - .with_system(component.system().label("3").after("1").after("2")); + .with_system(component.label("0").before("1").before("2")) + .with_system(component.label("1")) + .with_system(component.label("2")) + .with_system(component.label("3").after("1").after("2")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1755,20 +1701,20 @@ mod tests { assert_eq!(ambiguities.len(), 1); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0").before("1").before("2")) - .with_system(component.system().label("1").in_ambiguity_set("a")) - .with_system(component.system().label("2").in_ambiguity_set("a")) - .with_system(component.system().label("3").after("1").after("2")); + .with_system(component.label("0").before("1").before("2")) + .with_system(component.label("1").in_ambiguity_set("a")) + .with_system(component.label("2").in_ambiguity_set("a")) + .with_system(component.label("3").after("1").after("2")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); assert_eq!(ambiguities.len(), 0); let mut stage = SystemStage::parallel() - .with_system(component.system().label("0").before("1").before("2")) - .with_system(component.system().label("1").in_ambiguity_set("a")) - .with_system(component.system().label("2").in_ambiguity_set("b")) - .with_system(component.system().label("3").after("1").after("2")); + .with_system(component.label("0").before("1").before("2")) + .with_system(component.label("1").in_ambiguity_set("a")) + .with_system(component.label("2").in_ambiguity_set("b")) + .with_system(component.label("3").after("1").after("2")); stage.initialize_systems(&mut world); stage.rebuild_orders_and_dependencies(); let ambiguities = find_ambiguities_first_labels(&stage.parallel); @@ -1781,20 +1727,18 @@ mod tests { let mut stage = SystemStage::parallel() .with_system( component - .system() .label("0") .before("1") .before("2") .before("3") .before("4"), ) - .with_system(component.system().label("1")) - .with_system(component.system().label("2")) - .with_system(component.system().label("3")) - .with_system(component.system().label("4")) + .with_system(component.label("1")) + .with_system(component.label("2")) + .with_system(component.label("3")) + .with_system(component.label("4")) .with_system( component - .system() .label("5") .after("1") .after("2") @@ -1833,20 +1777,18 @@ mod tests { let mut stage = SystemStage::parallel() .with_system( component - .system() .label("0") .before("1") .before("2") .before("3") .before("4"), ) - .with_system(component.system().label("1").in_ambiguity_set("a")) - .with_system(component.system().label("2").in_ambiguity_set("a")) - .with_system(component.system().label("3").in_ambiguity_set("a")) - .with_system(component.system().label("4").in_ambiguity_set("a")) + .with_system(component.label("1").in_ambiguity_set("a")) + .with_system(component.label("2").in_ambiguity_set("a")) + .with_system(component.label("3").in_ambiguity_set("a")) + .with_system(component.label("4").in_ambiguity_set("a")) .with_system( component - .system() .label("5") .after("1") .after("2") @@ -1861,26 +1803,23 @@ mod tests { let mut stage = SystemStage::parallel() .with_system( component - .system() .label("0") .before("1") .before("2") .before("3") .before("4"), ) - .with_system(component.system().label("1").in_ambiguity_set("a")) - .with_system(component.system().label("2").in_ambiguity_set("a")) + .with_system(component.label("1").in_ambiguity_set("a")) + .with_system(component.label("2").in_ambiguity_set("a")) .with_system( component - .system() .label("3") .in_ambiguity_set("a") .in_ambiguity_set("b"), ) - .with_system(component.system().label("4").in_ambiguity_set("b")) + .with_system(component.label("4").in_ambiguity_set("b")) .with_system( component - .system() .label("5") .after("1") .after("2") @@ -2011,7 +1950,7 @@ mod tests { let mut world = World::new(); world.insert_resource(0_usize); - let mut stage = SystemStage::single(query_count_system.system()); + let mut stage = SystemStage::single(query_count_system); let entity = world.spawn().insert_bundle(()).id(); stage.run(&mut world); @@ -2034,7 +1973,7 @@ mod tests { let mut world = World::new(); world.insert_resource(0_usize); let mut stage = SystemStage::parallel(); - stage.add_system(query_count_system.system()); + stage.add_system(query_count_system); let entity = world.spawn().insert_bundle(()).id(); stage.run(&mut world); @@ -2056,7 +1995,7 @@ mod tests { let mut stage = SystemStage::parallel(); fn work() {} - stage.add_system(work.system()); + stage.add_system(work); // Overflow twice for _ in 0..10 { @@ -2134,11 +2073,11 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(spawn_entity.system().label("spawn")) + .with_system(spawn_entity.label("spawn")) .with_system_set( SystemSet::new() - .with_run_criteria(even_number_of_entities_critiera.system()) - .with_system(count_entities.system().before("spawn")), + .with_run_criteria(even_number_of_entities_critiera) + .with_system(count_entities.before("spawn")), ); stage.run(&mut world); stage.run(&mut world); @@ -2169,10 +2108,10 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); - let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity.system()); + let mut stage_spawn = SystemStage::parallel().with_system(spawn_entity); let mut stage_count = SystemStage::parallel() - .with_run_criteria(even_number_of_entities_critiera.system()) - .with_system(count_entities.system()); + .with_run_criteria(even_number_of_entities_critiera) + .with_system(count_entities); stage_count.run(&mut world); stage_spawn.run(&mut world); stage_count.run(&mut world); diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index d80fb7970dd46..c18b6fecdb202 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -4,7 +4,7 @@ use crate::{ RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun, SystemSet, }, - system::{In, IntoChainSystem, IntoSystem, Local, Res, ResMut}, + system::{ConfigurableSystem, In, IntoChainSystem, Local, Res, ResMut}, }; use std::{any::TypeId, fmt::Debug, hash::Hash}; use thiserror::Error; @@ -97,9 +97,8 @@ where (|state: Res>, pred: Local>| { state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none() }) - .system() .config(|(_, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::Update.into_label(s)) } @@ -118,9 +117,8 @@ where Some(_) => false, None => *is_inactive, }) - .system() .config(|(_, _, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(s)) } @@ -151,9 +149,8 @@ where Some(_) => false, None => *is_in_stack, }) - .system() .config(|(_, _, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(s)) } @@ -171,9 +168,8 @@ where _ => false, }) }) - .system() .config(|(_, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::Enter.into_label(s)) } @@ -189,9 +185,8 @@ where _ => false, }) }) - .system() .config(|(_, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::Exit.into_label(s)) } @@ -206,9 +201,8 @@ where _ => false, }) }) - .system() .config(|(_, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::Pause.into_label(s)) } @@ -223,9 +217,8 @@ where _ => false, }) }) - .system() .config(|(_, pred)| *pred = Some(Some(s.clone()))) - .chain(should_run_adapter::.system()) + .chain(should_run_adapter::) .after(DriverLabel::of::()) .label_discard_if_duplicate(StateCallback::Resume.into_label(s)) } @@ -259,8 +252,7 @@ where /// Important note: this set must be inserted **before** all other state-dependant sets to work /// properly! pub fn get_driver() -> SystemSet { - SystemSet::default() - .with_run_criteria(state_cleaner::.system().label(DriverLabel::of::())) + SystemSet::default().with_run_criteria(state_cleaner::.label(DriverLabel::of::())) } pub fn new(initial: T) -> Self { @@ -516,81 +508,63 @@ mod test { stage .add_system_set( State::on_enter_set(MyState::S1) - .with_system((|mut r: ResMut>| r.push("startup")).system()), - ) - .add_system_set( - State::on_update_set(MyState::S1).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S1"); - s.overwrite_replace(MyState::S2).unwrap(); - }) - .system(), - ), + .with_system(|mut r: ResMut>| r.push("startup")), ) + .add_system_set(State::on_update_set(MyState::S1).with_system( + |mut r: ResMut>, mut s: ResMut>| { + r.push("update S1"); + s.overwrite_replace(MyState::S2).unwrap(); + }, + )) .add_system_set( State::on_enter_set(MyState::S2) - .with_system((|mut r: ResMut>| r.push("enter S2")).system()), - ) - .add_system_set( - State::on_update_set(MyState::S2).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S2"); - s.overwrite_replace(MyState::S3).unwrap(); - }) - .system(), - ), + .with_system(|mut r: ResMut>| r.push("enter S2")), ) + .add_system_set(State::on_update_set(MyState::S2).with_system( + |mut r: ResMut>, mut s: ResMut>| { + r.push("update S2"); + s.overwrite_replace(MyState::S3).unwrap(); + }, + )) .add_system_set( State::on_exit_set(MyState::S2) - .with_system((|mut r: ResMut>| r.push("exit S2")).system()), + .with_system(|mut r: ResMut>| r.push("exit S2")), ) .add_system_set( State::on_enter_set(MyState::S3) - .with_system((|mut r: ResMut>| r.push("enter S3")).system()), - ) - .add_system_set( - State::on_update_set(MyState::S3).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S3"); - s.overwrite_push(MyState::S4).unwrap(); - }) - .system(), - ), + .with_system(|mut r: ResMut>| r.push("enter S3")), ) + .add_system_set(State::on_update_set(MyState::S3).with_system( + |mut r: ResMut>, mut s: ResMut>| { + r.push("update S3"); + s.overwrite_push(MyState::S4).unwrap(); + }, + )) .add_system_set( State::on_pause_set(MyState::S3) - .with_system((|mut r: ResMut>| r.push("pause S3")).system()), - ) - .add_system_set( - State::on_update_set(MyState::S4).with_system( - (|mut r: ResMut>, mut s: ResMut>| { - r.push("update S4"); - s.overwrite_push(MyState::S5).unwrap(); - }) - .system(), - ), - ) - .add_system_set( - State::on_inactive_update_set(MyState::S4).with_system( - (|mut r: ResMut>| r.push("inactive S4")) - .system() - .label("inactive s4"), - ), + .with_system(|mut r: ResMut>| r.push("pause S3")), ) + .add_system_set(State::on_update_set(MyState::S4).with_system( + |mut r: ResMut>, mut s: ResMut>| { + r.push("update S4"); + s.overwrite_push(MyState::S5).unwrap(); + }, + )) + .add_system_set(State::on_inactive_update_set(MyState::S4).with_system( + (|mut r: ResMut>| r.push("inactive S4")).label("inactive s4"), + )) .add_system_set( State::on_update_set(MyState::S5).with_system( (|mut r: ResMut>, mut s: ResMut>| { r.push("update S5"); s.overwrite_push(MyState::S6).unwrap(); }) - .system() .after("inactive s4"), ), ) .add_system_set( State::on_inactive_update_set(MyState::S5).with_system( (|mut r: ResMut>| r.push("inactive S5")) - .system() .label("inactive s5") .after("inactive s4"), ), @@ -601,17 +575,16 @@ mod test { r.push("update S6"); s.overwrite_push(MyState::Final).unwrap(); }) - .system() .after("inactive s5"), ), ) .add_system_set( State::on_resume_set(MyState::S4) - .with_system((|mut r: ResMut>| r.push("resume S4")).system()), + .with_system(|mut r: ResMut>| r.push("resume S4")), ) .add_system_set( State::on_exit_set(MyState::S5) - .with_system((|mut r: ResMut>| r.push("exit S4")).system()), + .with_system(|mut r: ResMut>| r.push("exit S4")), ); const EXPECTED: &[&str] = &[ @@ -671,7 +644,7 @@ mod test { world.insert_resource(State::new(AppState::Main)); world.insert_resource(false); world.insert_resource("control"); - let mut stage = SystemStage::parallel().with_system(should_run_once.system()); + let mut stage = SystemStage::parallel().with_system(should_run_once); stage.run(&mut world); assert!(*world.get_resource::().unwrap(), "after control"); @@ -679,7 +652,7 @@ mod test { world.insert_resource("test"); let mut stage = SystemStage::parallel() .with_system_set(State::::get_driver()) - .with_system(should_run_once.system()); + .with_system(should_run_once); stage.run(&mut world); assert!(*world.get_resource::().unwrap(), "after test"); } diff --git a/crates/bevy_ecs/src/schedule/system_container.rs b/crates/bevy_ecs/src/schedule/system_container.rs index 727c007e68b39..68e493da714b5 100644 --- a/crates/bevy_ecs/src/schedule/system_container.rs +++ b/crates/bevy_ecs/src/schedule/system_container.rs @@ -36,7 +36,7 @@ pub(super) struct ExclusiveSystemContainer { } impl ExclusiveSystemContainer { - pub fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self { + pub(super) fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self { ExclusiveSystemContainer { system: descriptor.system, run_criteria_index: None, @@ -49,7 +49,7 @@ impl ExclusiveSystemContainer { } } - pub fn system_mut(&mut self) -> &mut Box { + pub(super) fn system_mut(&mut self) -> &mut Box { &mut self.system } } diff --git a/crates/bevy_ecs/src/schedule/system_descriptor.rs b/crates/bevy_ecs/src/schedule/system_descriptor.rs index b75fbcb89f3db..74a3963ca26d7 100644 --- a/crates/bevy_ecs/src/schedule/system_descriptor.rs +++ b/crates/bevy_ecs/src/schedule/system_descriptor.rs @@ -30,8 +30,8 @@ use crate::{ /// struct Something; /// /// SystemStage::parallel() -/// .with_system(do_something.system().label(Something)) -/// .with_system(do_the_other_thing.system().after(Something)) +/// .with_system(do_something.label(Something)) +/// .with_system(do_the_other_thing.after(Something)) /// .with_system(do_something_else.exclusive_system().at_end()); /// ``` pub enum SystemDescriptor { @@ -60,6 +60,12 @@ where } } +impl IntoSystemDescriptor<()> for SystemDescriptor { + fn into_descriptor(self) -> SystemDescriptor { + self + } +} + impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> { fn into_descriptor(self) -> SystemDescriptor { new_parallel_descriptor(self).into_descriptor() diff --git a/crates/bevy_ecs/src/storage/blob_vec.rs b/crates/bevy_ecs/src/storage/blob_vec.rs index d724728c177b2..49c92d92d1eb9 100644 --- a/crates/bevy_ecs/src/storage/blob_vec.rs +++ b/crates/bevy_ecs/src/storage/blob_vec.rs @@ -267,9 +267,13 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize { #[cfg(test)] mod tests { use super::BlobVec; - use crate::component::TypeInfo; use std::{alloc::Layout, cell::RefCell, rc::Rc}; + // SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value. + unsafe fn drop_ptr(x: *mut u8) { + x.cast::().drop_in_place() + } + /// # Safety /// /// `blob_vec` must have a layout that matches Layout::new::() @@ -300,7 +304,7 @@ mod tests { #[test] fn resize_test() { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let mut blob_vec = BlobVec::new(item_layout, drop, 64); unsafe { for i in 0..1_000 { @@ -330,7 +334,7 @@ mod tests { let drop_counter = Rc::new(RefCell::new(0)); { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let mut blob_vec = BlobVec::new(item_layout, drop, 2); assert_eq!(blob_vec.capacity(), 2); unsafe { @@ -390,7 +394,7 @@ mod tests { #[test] fn blob_vec_drop_empty_capacity() { let item_layout = Layout::new::(); - let drop = TypeInfo::drop_ptr::; + let drop = drop_ptr::; let _ = BlobVec::new(item_layout, drop, 0); } } diff --git a/crates/bevy_ecs/src/storage/table.rs b/crates/bevy_ecs/src/storage/table.rs index 9c041f8174eef..60d41f25ad127 100644 --- a/crates/bevy_ecs/src/storage/table.rs +++ b/crates/bevy_ecs/src/storage/table.rs @@ -522,17 +522,12 @@ impl IndexMut for Tables { #[cfg(test)] mod tests { - use crate::{ - component::{Components, TypeInfo}, - entity::Entity, - storage::Table, - }; + use crate::{component::Components, entity::Entity, storage::Table}; #[test] fn table() { let mut components = Components::default(); - let type_info = TypeInfo::of::(); - let component_id = components.get_or_insert_with(type_info.type_id(), || type_info); + let component_id = components.get_or_insert_id::(); let columns = &[component_id]; let mut table = Table::with_capacity(0, columns.len()); table.add_column(components.get_info(component_id).unwrap()); diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index fe6addbd65b98..fc875f383c6bb 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -6,7 +6,7 @@ use crate::{ entity::{Entities, Entity}, world::World, }; -use bevy_utils::tracing::debug; +use bevy_utils::tracing::{error, warn}; pub use command_queue::CommandQueue; use std::marker::PhantomData; @@ -16,12 +16,12 @@ pub trait Command: Send + Sync + 'static { } /// A list of commands that will be run to modify a [`World`]. -pub struct Commands<'s, 'w> { +pub struct Commands<'w, 's> { queue: &'s mut CommandQueue, entities: &'w Entities, } -impl<'s, 'w> Commands<'s, 'w> { +impl<'w, 's> Commands<'w, 's> { /// Create a new `Commands` from a queue and a world. pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self { Self { @@ -50,7 +50,7 @@ impl<'s, 'w> Commands<'s, 'w> { /// } /// # example_system.system(); /// ``` - pub fn spawn(&mut self) -> EntityCommands<'s, 'w, '_> { + pub fn spawn<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> { let entity = self.entities.reserve_entity(); EntityCommands { entity, @@ -58,7 +58,14 @@ impl<'s, 'w> Commands<'s, 'w> { } } - pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> { + /// Returns an [EntityCommands] for the given `entity` (if it exists) or spawns one if it doesn't exist. + /// This will return [None] if the `entity` exists with a different generation. + /// + /// # Note + /// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`Commands::spawn`]. + /// This method should generally only be used for sharing entities across apps, and only when they have a + /// scheme worked out to share an ID space (which doesn't happen by default). + pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> { self.add(GetOrSpawn { entity }); EntityCommands { entity, @@ -66,9 +73,8 @@ impl<'s, 'w> Commands<'s, 'w> { } } - // TODO: this is a hack to work around the "multiple worlds" limitations: - // Right now Commands must allocate entities from their "scheduled" world, but Commands might be applied to other worlds, - // such as the "render world" + /// Spawns a [Bundle] without pre-allocating an [Entity]. The [Entity] will be allocated when + /// this [Command] is applied. pub fn spawn_and_forget(&mut self, bundle: impl Bundle) { self.queue.push(Spawn { bundle }) } @@ -113,7 +119,7 @@ impl<'s, 'w> Commands<'s, 'w> { /// } /// # example_system.system(); /// ``` - pub fn spawn_bundle(&mut self, bundle: T) -> EntityCommands<'s, 'w, '_> { + 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 @@ -138,7 +144,13 @@ impl<'s, 'w> Commands<'s, 'w> { /// } /// # example_system.system(); /// ``` - pub fn entity(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> { + #[track_caller] + pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> { + assert!( + self.entities.contains(entity), + "Attempting to create an EntityCommands for entity {:?}, which doesn't exist.", + entity + ); EntityCommands { entity, commands: self, @@ -155,6 +167,23 @@ impl<'s, 'w> Commands<'s, 'w> { self.queue.push(SpawnBatch { bundles_iter }); } + /// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given + /// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists). + /// This is faster than doing equivalent operations one-by-one. + /// + /// # Note + /// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`Commands::spawn_batch`]. + /// This method should generally only be used for sharing entities across apps, and only when they have a scheme + /// worked out to share an ID space (which doesn't happen by default). + pub fn insert_or_spawn_batch(&mut self, bundles_iter: I) + where + I: IntoIterator + Send + Sync + 'static, + I::IntoIter: Iterator, + B: Bundle, + { + self.queue.push(InsertOrSpawnBatch { bundles_iter }); + } + /// See [`World::insert_resource`]. pub fn insert_resource(&mut self, resource: T) { self.queue.push(InsertResource { resource }) @@ -174,12 +203,12 @@ impl<'s, 'w> Commands<'s, 'w> { } /// A list of commands that will be run to modify an [`Entity`]. -pub struct EntityCommands<'s, 'w, 'a> { +pub struct EntityCommands<'w, 's, 'a> { entity: Entity, - commands: &'a mut Commands<'s, 'w>, + commands: &'a mut Commands<'w, 's>, } -impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> { +impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> { /// Retrieves the current entity's unique [`Entity`] id. #[inline] pub fn id(&self) -> Entity { @@ -267,7 +296,7 @@ impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> { } /// Returns the underlying `[Commands]`. - pub fn commands(&mut self) -> &mut Commands<'s, 'w> { + pub fn commands(&mut self) -> &mut Commands<'w, 's> { self.commands } } @@ -314,6 +343,32 @@ where } } +pub struct InsertOrSpawnBatch +where + I: IntoIterator + Send + Sync + 'static, + B: Bundle, + I::IntoIter: Iterator, +{ + pub bundles_iter: I, +} + +impl Command for InsertOrSpawnBatch +where + I: IntoIterator + Send + Sync + 'static, + B: Bundle, + I::IntoIter: Iterator, +{ + fn write(self, world: &mut World) { + if let Err(invalid_entities) = world.insert_or_spawn_batch(self.bundles_iter) { + error!( + "Failed to 'insert or spawn' bundle of type {} into the following invalid entities: {:?}", + std::any::type_name::(), + invalid_entities + ); + } + } +} + #[derive(Debug)] pub struct Despawn { pub entity: Entity, @@ -322,7 +377,9 @@ pub struct Despawn { impl Command for Despawn { fn write(self, world: &mut World) { if !world.despawn(self.entity) { - debug!("Failed to despawn non-existent entity {:?}", self.entity); + warn!("Could not despawn entity {:?} because it doesn't exist in this World.\n\ + If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\ + This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", self.entity); } } } @@ -337,7 +394,13 @@ where T: Bundle + 'static, { fn write(self, world: &mut World) { - world.entity_mut(self.entity).insert_bundle(self.bundle); + if let Some(mut entity) = world.get_entity_mut(self.entity) { + entity.insert_bundle(self.bundle); + } else { + panic!("Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.\n\ + If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\ + This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::(), self.entity); + } } } @@ -352,7 +415,13 @@ where T: Component, { fn write(self, world: &mut World) { - world.entity_mut(self.entity).insert(self.component); + if let Some(mut entity) = world.get_entity_mut(self.entity) { + entity.insert(self.component); + } else { + panic!("Could not add a component (of type `{}`) to entity {:?} because it doesn't exist in this World.\n\ + If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\ + This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::(), self.entity); + } } } diff --git a/crates/bevy_ecs/src/system/exclusive_system.rs b/crates/bevy_ecs/src/system/exclusive_system.rs index 8ea5064167b4b..7050355b6b8fe 100644 --- a/crates/bevy_ecs/src/system/exclusive_system.rs +++ b/crates/bevy_ecs/src/system/exclusive_system.rs @@ -96,6 +96,7 @@ impl ExclusiveSystem for ExclusiveSystemCoerced { for archetype in archetypes.archetypes[archetype_index_range].iter() { self.system.new_archetype(archetype); } + self.system.run((), world); self.system.apply_buffers(world); } @@ -127,7 +128,7 @@ mod tests { entity::Entity, query::With, schedule::{Stage, SystemStage}, - system::{Commands, IntoExclusiveSystem, IntoSystem, Query, ResMut}, + system::{Commands, IntoExclusiveSystem, Query, ResMut}, world::World, }; #[test] @@ -145,7 +146,7 @@ mod tests { } } - let mut stage = SystemStage::parallel().with_system(removal.system()); + let mut stage = SystemStage::parallel().with_system(removal); world.spawn().insert(0.0f32); world.insert_resource(0usize); stage.run(&mut world); @@ -175,7 +176,7 @@ mod tests { let mut world = World::new(); world.insert_resource(Vec::::new()); let mut stage = SystemStage::parallel() - .with_system(spawn_entity.system()) + .with_system(spawn_entity) .with_system(count_entities.exclusive_system()); stage.run(&mut world); stage.run(&mut world); diff --git a/crates/bevy_ecs/src/system/function_system.rs b/crates/bevy_ecs/src/system/function_system.rs index 9a4b531362ac3..c37653c078567 100644 --- a/crates/bevy_ecs/src/system/function_system.rs +++ b/crates/bevy_ecs/src/system/function_system.rs @@ -24,7 +24,7 @@ pub struct SystemMeta { } impl SystemMeta { - pub fn new() -> Self { + fn new() -> Self { Self { name: std::any::type_name::().into(), archetype_component_access: Access::default(), @@ -87,10 +87,10 @@ impl SystemState { /// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only. #[inline] - pub fn get<'s, 'w>( + pub fn get<'w, 's>( &'s mut self, world: &'w World, - ) -> >::Item + ) -> >::Item where Param::Fetch: ReadOnlySystemParamFetch, { @@ -101,10 +101,10 @@ impl SystemState { /// Retrieve the mutable [`SystemParam`] values. #[inline] - pub fn get_mut<'s, 'w>( + pub fn get_mut<'w, 's>( &'s mut self, world: &'w mut World, - ) -> >::Item { + ) -> >::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) } @@ -145,10 +145,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<'s, 'w>( + pub unsafe fn get_unchecked_manual<'w, 's>( &'s mut self, world: &'w World, - ) -> >::Item { + ) -> >::Item { let change_tick = world.increment_change_tick(); let param = ::get_param( &mut self.param_state, @@ -228,7 +228,7 @@ pub struct InputMarker; /// /// You get this by calling [`IntoSystem::system`] on a function that only accepts /// [`SystemParam`]s. The output of the system becomes the functions return type, while the input -/// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists. +/// becomes the functions [`In`] tagged parameter or `()` if no such parameter exists. pub struct FunctionSystem where Param: SystemParam, @@ -254,7 +254,7 @@ impl FunctionSystem) { /// assert_eq!(*local, 42); /// } - /// let mut system = local_is_42.system().config(|config| config.0 = Some(42)); + /// let mut system = local_is_42.config(|config| config.0 = Some(42)); /// system.initialize(world); /// system.run((), world); /// ``` diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 2110bad7520c9..7bed6a47125e7 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -24,11 +24,11 @@ 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, - RemovedComponents, Res, ResMut, System, SystemState, + ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, Query, + QuerySet, RemovedComponents, Res, ResMut, System, SystemState, }, world::{FromWorld, World}, }; @@ -60,7 +60,7 @@ mod tests { system.run((), &mut world); } - fn run_system>(world: &mut World, system: S) { + fn run_system>(world: &mut World, system: S) { let mut schedule = Schedule::default(); let mut update = SystemStage::parallel(); update.add_system(system); @@ -121,7 +121,7 @@ mod tests { world.spawn().insert_bundle((A, C)); world.spawn().insert_bundle((A, D)); - run_system(&mut world, query_system.system()); + run_system(&mut world, query_system); assert!(*world.get_resource::().unwrap(), "system ran"); } @@ -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(); @@ -149,7 +149,7 @@ mod tests { world.insert_resource(false); world.spawn().insert_bundle((A, B)); - run_system(&mut world, query_system.system()); + run_system(&mut world, query_system); assert!(*world.get_resource::().unwrap(), "system ran"); } @@ -179,7 +179,7 @@ mod tests { let mut schedule = Schedule::default(); let mut update = SystemStage::parallel(); - update.add_system(incr_e_on_flip.system()); + update.add_system(incr_e_on_flip); schedule.add_stage("update", update); schedule.add_stage( "clear_trackers", @@ -206,7 +206,7 @@ mod tests { fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {} let mut world = World::default(); - run_system(&mut world, sys.system()); + run_system(&mut world, sys); } #[test] @@ -214,7 +214,7 @@ mod tests { fn sys(_q1: Query<&mut A, With>, _q2: Query<&mut A, Without>) {} let mut world = World::default(); - run_system(&mut world, sys.system()); + run_system(&mut world, sys); } #[test] @@ -222,7 +222,7 @@ mod tests { fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without>) {} let mut world = World::default(); - run_system(&mut world, sys.system()); + run_system(&mut world, sys); } #[test] @@ -231,32 +231,36 @@ mod tests { fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {} let mut world = World::default(); - run_system(&mut world, sys.system()); + run_system(&mut world, sys); } #[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.system()); + run_system(&mut world, sys); } #[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.system()); + run_system(&mut world, sys); } #[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.system()); + run_system(&mut world, sys); } #[derive(Default)] @@ -264,39 +268,39 @@ mod tests { _buffer: Vec, } - fn test_for_conflicting_resources>(sys: S) { + fn test_for_conflicting_resources>(sys: S) { let mut world = World::default(); world.insert_resource(BufferRes::default()); world.insert_resource(A); world.insert_resource(B); - run_system(&mut world, sys.system()); + run_system(&mut world, sys); } #[test] #[should_panic] fn conflicting_system_resources() { fn sys(_: ResMut, _: Res) {} - test_for_conflicting_resources(sys.system()) + test_for_conflicting_resources(sys) } #[test] #[should_panic] fn conflicting_system_resources_reverse_order() { fn sys(_: Res, _: ResMut) {} - test_for_conflicting_resources(sys.system()) + test_for_conflicting_resources(sys) } #[test] #[should_panic] fn conflicting_system_resources_multiple_mutable() { fn sys(_: ResMut, _: ResMut) {} - test_for_conflicting_resources(sys.system()) + test_for_conflicting_resources(sys) } #[test] fn nonconflicting_system_resources() { fn sys(_: Local, _: ResMut, _: Local, _: ResMut) {} - test_for_conflicting_resources(sys.system()) + test_for_conflicting_resources(sys) } #[test] @@ -321,12 +325,54 @@ mod tests { *modified = true; } - run_system(&mut world, sys.system()); + run_system(&mut world, sys); + + // ensure the system actually ran + assert!(*world.get_resource::().unwrap()); + } + + #[test] + fn non_send_option_system() { + let mut world = World::default(); + + world.insert_resource(false); + struct NotSend1(std::rc::Rc); + struct NotSend2(std::rc::Rc); + world.insert_non_send(NotSend1(std::rc::Rc::new(0))); + + fn sys( + op: Option>, + mut _op2: Option>, + mut run: ResMut, + ) { + op.expect("NonSend should exist"); + *run = true; + } + run_system(&mut world, sys); // ensure the system actually ran assert!(*world.get_resource::().unwrap()); } + #[test] + fn non_send_system() { + let mut world = World::default(); + + world.insert_resource(false); + struct NotSend1(std::rc::Rc); + struct NotSend2(std::rc::Rc); + + world.insert_non_send(NotSend1(std::rc::Rc::new(1))); + world.insert_non_send(NotSend2(std::rc::Rc::new(2))); + + fn sys(_op: NonSend, mut _op2: NonSendMut, mut run: ResMut) { + *run = true; + } + + run_system(&mut world, sys); + assert!(*world.get_resource::().unwrap()); + } + #[test] fn remove_tracking() { let mut world = World::new(); @@ -352,7 +398,7 @@ mod tests { *ran = true; } - run_system(&mut world, validate_removed.system()); + run_system(&mut world, validate_removed); assert!(*world.get_resource::().unwrap(), "system ran"); } @@ -365,18 +411,10 @@ mod tests { *modified = true; } - run_system( - &mut world, - sys.system().config(|config| config.0 = Some(42)), - ); + run_system(&mut world, sys.config(|config| config.0 = Some(42))); // ensure the system actually ran assert!(*world.get_resource::().unwrap()); - - // Now do the same with omitted `.system()`. - world.insert_resource(false); - run_system(&mut world, sys.config(|config| config.0 = Some(42))); - assert!(*world.get_resource::().unwrap()); } #[test] @@ -417,7 +455,7 @@ mod tests { *modified = true; } - run_system(&mut world, sys.system()); + run_system(&mut world, sys); // ensure the system actually ran assert!(*world.get_resource::().unwrap()); @@ -528,12 +566,15 @@ 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!( - *query.single().unwrap(), + *query.single(), B(7), "returned component matches initial value" ); @@ -560,7 +601,7 @@ mod tests { let (a, mut query) = system_state.get_mut(&mut world); assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!( - *query.single_mut().unwrap(), + *query.single_mut(), B(7), "returned component matches initial value" ); @@ -577,18 +618,18 @@ mod tests { let mut system_state: SystemState>> = SystemState::new(&mut world); { let query = system_state.get(&world); - assert_eq!(*query.single().unwrap(), A(1)); + assert_eq!(*query.single(), A(1)); } { let query = system_state.get(&world); - assert!(query.single().is_err()); + assert!(query.get_single().is_err()); } world.entity_mut(entity).get_mut::().unwrap().0 = 2; { let query = system_state.get(&world); - assert_eq!(*query.single().unwrap(), A(2)); + assert_eq!(*query.single(), A(2)); } } @@ -632,4 +673,199 @@ mod tests { ); } } + + /// this test exists to show that read-only world-only queries can return data that lives as long as 'world + #[test] + #[allow(unused)] + 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 + } + } + } } + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(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); +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_iter_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(usize); +/// fn system(mut query: Query<&mut A>, e: Res) { +/// 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); +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_get_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(usize); +/// 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(); +/// +/// let q1 = queries.q1(); +/// let mut iter = q1.iter(); +/// let a = &*iter.next().unwrap(); +/// +/// // this should fail to compile +/// b.0 = a.0 +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_set_iter_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(usize); +/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { +/// let q1 = queries.q1(); +/// let mut iter = q1.iter(); +/// let a = &*iter.next().unwrap(); +/// +/// let mut q2 = queries.q0(); +/// let mut iter2 = q2.iter_mut(); +/// let mut b = iter2.next().unwrap(); +/// +/// // this should fail to compile +/// b.0 = a.0; +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_set_iter_flip_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(usize); +/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { +/// let mut q2 = queries.q0(); +/// 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 +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_set_get_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// struct A(usize); +/// fn query_set(mut queries: QuerySet<(QueryState<&mut A>, QueryState<&A>)>, e: Res) { +/// let q1 = queries.q1(); +/// let a = q1.get(*e).unwrap(); +/// +/// let mut q2 = queries.q0(); +/// let mut b = q2.get_mut(*e).unwrap(); +/// // this should fail to compile +/// b.0 = a.0 +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_query_set_get_flip_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::system::SystemState; +/// 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); +/// } +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_state_get_lifetime_safety_test() {} + +/// ```compile_fail +/// use bevy_ecs::prelude::*; +/// use bevy_ecs::system::SystemState; +/// struct A(usize); +/// struct B(usize); +/// struct State { +/// state_r: SystemState>, +/// state_w: SystemState>, +/// } +/// +/// impl State { +/// 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); +/// } +/// } +/// ``` +#[allow(unused)] +#[cfg(doc)] +fn system_state_iter_lifetime_safety_test() {} diff --git a/crates/bevy_ecs/src/system/param_state.rs b/crates/bevy_ecs/src/system/param_state.rs deleted file mode 100644 index 8721edbe1ca83..0000000000000 --- a/crates/bevy_ecs/src/system/param_state.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::system::{SystemParam, SystemParamFetch, SystemParamState, SystemState}; -use crate::world::World; - -pub struct ParamState { - state: SystemState, - param_state: ::Fetch, - change_tick: u32, -} - -impl ParamState { - pub fn new(world: &mut World) -> Self { - let mut state = SystemState::new::(); - let config = ::default_config(); - let param_state = ::init(world, &mut state, config); - Self { - state, - param_state, - change_tick: 0, - } - } - - // TODO: THIS IS SUPER UNSAFE PLEASE DON'T MERGE UNTIL IT IS CONSTRAINED TO READ-ONLY PARAMS - pub fn get<'a>(&'a mut self, world: &'a World) -> >::Item { - let change_tick = world.increment_change_tick(); - self.change_tick = change_tick; - // TODO: add/implement ReadOnlySystemParam and constrain param here - unsafe { - ::get_param( - &mut self.param_state, - &mut self.state, - world, - change_tick, - ) - } - } -} diff --git a/crates/bevy_ecs/src/system/query.rs b/crates/bevy_ecs/src/system/query.rs index 5317aa3c37e19..921454ec2c973 100644 --- a/crates/bevy_ecs/src/system/query.rs +++ b/crates/bevy_ecs/src/system/query.rs @@ -107,12 +107,12 @@ 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, 's, 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: &'s QueryState, + pub(crate) world: &'world World, + pub(crate) state: &'state QueryState, pub(crate) last_change_tick: u32, pub(crate) change_tick: u32, } @@ -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<'w, 's, Q, F> + pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F> where Q::Fetch: ReadOnlyFetch, { @@ -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 /// @@ -166,7 +177,7 @@ where /// - if K < N: all possible K-sized combinations of query results, without repetition /// - if K > N: empty set (no K-sized combinations exist) #[inline] - pub fn iter_combinations(&self) -> QueryCombinationIter<'w, 's, Q, F, K> + pub fn iter_combinations(&self) -> QueryCombinationIter<'_, '_, Q, F, K> where Q::Fetch: ReadOnlyFetch, { @@ -181,17 +192,6 @@ where } } - /// Returns an [`Iterator`] over the query results. - #[inline] - pub fn iter_mut(&mut self) -> QueryIter<'w, 's, 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. @@ -216,7 +216,7 @@ where #[inline] pub fn iter_combinations_mut( &mut self, - ) -> QueryCombinationIter<'w, 's, Q, F, K> { + ) -> QueryCombinationIter<'_, '_, Q, F, K> { // SAFE: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { @@ -235,7 +235,7 @@ where /// This function makes it possible to violate Rust's aliasing guarantees. You must make sure /// this call does not result in multiple mutable references to the same component #[inline] - pub unsafe fn iter_unsafe(&self) -> QueryIter<'w, 's, Q, F> { + pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> { // SEMI-SAFE: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict self.state @@ -251,7 +251,7 @@ where #[inline] pub unsafe fn iter_combinations_unsafe( &self, - ) -> QueryCombinationIter<'w, 's, Q, F, K> { + ) -> QueryCombinationIter<'_, '_, Q, F, K> { // SEMI-SAFE: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict self.state.iter_combinations_unchecked_manual( @@ -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(&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(&mut self, f: impl FnMut(>::Item)) { + pub fn for_each_mut<'a>(&'a 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 { @@ -304,7 +304,7 @@ where /// write-queries. #[inline] pub fn par_for_each( - &self, + &'s self, task_pool: &TaskPool, batch_size: usize, f: impl Fn(>::Item) + Send + Sync + Clone, @@ -327,11 +327,11 @@ where /// Runs `f` on each query result in parallel using the given task pool. #[inline] - pub fn par_for_each_mut( - &mut self, + pub fn par_for_each_mut<'a>( + &'a 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(&self, entity: Entity) -> Result<>::Item, QueryEntityError> + pub fn get( + &'s self, + entity: Entity, + ) -> Result<>::Item, QueryEntityError> where Q::Fetch: ReadOnlyFetch, { @@ -372,7 +375,7 @@ where pub fn get_mut( &mut self, entity: Entity, - ) -> Result<>::Item, QueryEntityError> { + ) -> Result<::Item, QueryEntityError> { // SAFE: system runs without conflicts with other systems. // same-system queries have runtime borrow checks when they conflict unsafe { @@ -393,7 +396,7 @@ where /// this call does not result in multiple mutable references to the same component #[inline] pub unsafe fn get_unchecked( - &'s self, + &self, entity: Entity, ) -> Result<::Item, QueryEntityError> { // SEMI-SAFE: system runs without conflicts with other systems. @@ -442,7 +445,7 @@ where pub fn get_component_mut( &mut self, entity: Entity, - ) -> Result, QueryComponentError> { + ) -> Result, QueryComponentError> { // SAFE: unique access to query (preventing aliased access) unsafe { self.get_component_unchecked_mut(entity) } } @@ -459,7 +462,7 @@ where pub unsafe fn get_component_unchecked_mut( &self, entity: Entity, - ) -> Result, QueryComponentError> { + ) -> Result, QueryComponentError> { let world = self.world; let entity_ref = world .get_entity(entity) @@ -485,6 +488,33 @@ where } } + /// Gets the result of a single-result query. + /// + /// Assumes this query has only one result and panics if there are no or multiple results. + /// Use [`Self::get_single`] to handle the error cases explicitly + /// + /// # Example + /// + /// ``` + /// # use bevy_ecs::prelude::{IntoSystem, Query, With}; + /// struct Player; + /// struct Position(f32, f32); + /// fn player_system(query: Query<&Position, With>) { + /// let player_position = query.single(); + /// // do something with player_position + /// } + /// # let _check_that_its_a_system = player_system.system(); + /// ``` + /// + /// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries. + #[track_caller] + pub fn single(&'s self) -> >::Item + where + Q::Fetch: ReadOnlyFetch, + { + self.get_single().unwrap() + } + /// Gets the result of a single-result query. /// /// If the query has exactly one result, returns the result inside `Ok` @@ -494,27 +524,28 @@ where /// # Examples /// /// ``` + /// # use bevy_ecs::prelude::{IntoSystem, With}; /// # use bevy_ecs::system::{Query, QuerySingleError}; - /// # use bevy_ecs::prelude::IntoSystem; - /// struct PlayerScore(i32); - /// fn player_scoring_system(query: Query<&PlayerScore>) { - /// match query.single() { - /// Ok(PlayerScore(score)) => { - /// // do something with score + /// struct Player; + /// struct Position(f32, f32); + /// fn player_system(query: Query<&Position, With>) { + /// match query.get_single() { + /// Ok(position) => { + /// // do something with position /// } /// Err(QuerySingleError::NoEntities(_)) => { - /// // no PlayerScore + /// // no position with Player /// } /// Err(QuerySingleError::MultipleEntities(_)) => { - /// // multiple PlayerScore + /// // multiple position with Player /// } /// } /// } - /// # let _check_that_its_a_system = player_scoring_system.system(); + /// # let _check_that_its_a_system = player_system.system(); /// ``` /// - /// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries. - pub fn single(&self) -> Result<>::Item, QuerySingleError> + /// This can only be called for read-only queries, see [`Self::get_single_mut`] for write-queries. + pub fn get_single(&'s self) -> Result<>::Item, QuerySingleError> where Q::Fetch: ReadOnlyFetch, { @@ -531,9 +562,18 @@ where } } + /// Gets the query result if it is only a single result, otherwise panics + /// If you want to handle the error case yourself you can use the [`Self::get_single_mut`] variant. + #[track_caller] + pub fn single_mut(&mut self) -> >::Item { + self.get_single_mut().unwrap() + } + /// Gets the query result if it is only a single result, otherwise returns a /// [`QuerySingleError`]. - pub fn single_mut(&mut self) -> Result<>::Item, QuerySingleError> { + pub fn get_single_mut( + &mut self, + ) -> Result<>::Item, QuerySingleError> { let mut query = self.iter_mut(); let first = query.next(); let extra = query.next().is_some(); diff --git a/crates/bevy_ecs/src/system/system.rs b/crates/bevy_ecs/src/system/system.rs index 12b77135a14c2..eabf476730340 100644 --- a/crates/bevy_ecs/src/system/system.rs +++ b/crates/bevy_ecs/src/system/system.rs @@ -24,7 +24,7 @@ impl SystemId { /// /// Systems are functions with all arguments implementing [SystemParam](crate::system::SystemParam). /// -/// Systems are added to an application using `AppBuilder::add_system(my_system.system())` +/// Systems are added to an application using `App::add_system(my_system)` /// or similar methods, and will generally run once per pass of the main loop. /// /// Systems are executed in parallel, in opportunistic order; data access is managed automatically. diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 900017e9cbd1d..6f3e731ad3f22 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -27,13 +27,12 @@ use std::{ /// /// ``` /// # use bevy_ecs::prelude::*; -/// # use std::marker::PhantomData; +/// use std::marker::PhantomData; /// use bevy_ecs::system::SystemParam; /// /// #[derive(SystemParam)] -/// struct MyParam<'s, 'w> { +/// struct MyParam<'w, 's> { /// foo: Res<'w, usize>, -/// // TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes? /// #[system_param(ignore)] /// marker: PhantomData<&'s usize>, /// } @@ -45,7 +44,7 @@ use std::{ /// # my_system.system(); /// ``` pub trait SystemParam: Sized { - type Fetch: for<'s, 'w> SystemParamFetch<'s, 'w>; + type Fetch: for<'w, 's> SystemParamFetch<'w, 's>; } /// The state of a [`SystemParam`]. @@ -84,21 +83,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<'s, 'w>: 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: &'s mut Self, + state: &'state mut Self, system_meta: &SystemMeta, - world: &'w World, + world: &'world World, change_tick: u32, ) -> Self::Item; } -impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F> +impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F> where F::Fetch: FilterFetch, { @@ -150,7 +149,7 @@ where fn default_config() {} } -impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w> +impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's> for QueryState where F::Fetch: FilterFetch, @@ -189,7 +188,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!(); @@ -254,7 +259,7 @@ impl<'w, T: Component> AsRef for Res<'w, T> { } } -/// The [`SystemParamState`] of [`Res`]. +/// The [`SystemParamState`] of [`Res`]. pub struct ResState { component_id: ComponentId, marker: PhantomData, @@ -295,7 +300,7 @@ unsafe impl SystemParamState for ResState { fn default_config() {} } -impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState { +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState { type Item = Res<'w, T>; #[inline] @@ -323,7 +328,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState { } } -/// The [`SystemParamState`] of `Option>`. +/// The [`SystemParamState`] of [`Option>`]. +/// See: [`Res`] pub struct OptionResState(ResState); impl<'a, T: Component> SystemParam for Option> { @@ -343,7 +349,7 @@ unsafe impl SystemParamState for OptionResState { fn default_config() {} } -impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState { +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState { type Item = Option>; #[inline] @@ -364,7 +370,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState { } } -/// The [`SystemParamState`] of [`ResMut`]. +/// The [`SystemParamState`] of [`ResMut`]. pub struct ResMutState { component_id: ComponentId, marker: PhantomData, @@ -409,7 +415,7 @@ unsafe impl SystemParamState for ResMutState { fn default_config() {} } -impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState { +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState { type Item = ResMut<'w, T>; #[inline] @@ -439,7 +445,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState { } } -/// The [`SystemParamState`] of `Option>`. +/// The [`SystemParamState`] of [`Option>`]. +/// See: [`ResMut`] pub struct OptionResMutState(ResMutState); impl<'a, T: Component> SystemParam for Option> { @@ -456,7 +463,7 @@ unsafe impl SystemParamState for OptionResMutState { fn default_config() {} } -impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState { +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState { type Item = Option>; #[inline] @@ -479,7 +486,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState { } } -impl<'s, 'w> SystemParam for Commands<'s, 'w> { +impl<'w, 's> SystemParam for Commands<'w, 's> { type Fetch = CommandQueue; } @@ -501,8 +508,8 @@ unsafe impl SystemParamState for CommandQueue { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for CommandQueue { - type Item = Commands<'s, 'w>; +impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue { + type Item = Commands<'w, 's>; #[inline] unsafe fn get_param( @@ -571,7 +578,7 @@ impl<'a, T: Component> DerefMut for Local<'a, T> { } } -/// The [`SystemParamState`] of [`Local`]. +/// The [`SystemParamState`] of [`Local`]. pub struct LocalState(T); impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> { @@ -591,7 +598,7 @@ unsafe impl SystemParamState for LocalState { } } -impl<'s, 'w, T: Component + FromWorld> SystemParamFetch<'s, 'w> for LocalState { +impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState { type Item = Local<'s, T>; #[inline] @@ -639,7 +646,7 @@ impl<'a, T> RemovedComponents<'a, T> { // SAFE: Only reads World components unsafe impl ReadOnlySystemParamFetch for RemovedComponentsState {} -/// The [`SystemParamState`] of [`RemovedComponents`]. +/// The [`SystemParamState`] of [`RemovedComponents`]. pub struct RemovedComponentsState { component_id: ComponentId, marker: PhantomData, @@ -664,7 +671,7 @@ unsafe impl SystemParamState for RemovedComponentsState { fn default_config() {} } -impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for RemovedComponentsState { +impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState { type Item = RemovedComponents<'w, T>; #[inline] @@ -736,7 +743,7 @@ impl<'w, T: 'static> Deref for NonSend<'w, T> { } } -/// The [`SystemParamState`] of [`NonSend`]. +/// The [`SystemParamState`] of [`NonSend`]. pub struct NonSendState { component_id: ComponentId, marker: PhantomData T>, @@ -779,7 +786,7 @@ unsafe impl SystemParamState for NonSendState { fn default_config() {} } -impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState { +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState { type Item = NonSend<'w, T>; #[inline] @@ -809,10 +816,11 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState { } } -/// The [`SystemParamState`] of `Option>`. +/// The [`SystemParamState`] of [`Option>`]. +/// See: [`NonSend`] pub struct OptionNonSendState(NonSendState); -impl<'a, T: Component> SystemParam for Option> { +impl<'w, T: 'static> SystemParam for Option> { type Fetch = OptionNonSendState; } @@ -829,7 +837,7 @@ unsafe impl SystemParamState for OptionNonSendState { fn default_config() {} } -impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState { +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState { type Item = Option>; #[inline] @@ -851,7 +859,7 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState { } } -/// The [`SystemParamState`] of [`NonSendMut`]. +/// The [`SystemParamState`] of [`NonSendMut`]. pub struct NonSendMutState { component_id: ComponentId, marker: PhantomData T>, @@ -869,7 +877,7 @@ unsafe impl SystemParamState for NonSendMutState { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { system_meta.set_non_send(); - let component_id = world.components.get_or_insert_non_send_resource_id::(); + let component_id = world.initialize_non_send_resource::(); let combined_access = system_meta.component_access_set.combined_access_mut(); if combined_access.has_write(component_id) { panic!( @@ -898,7 +906,7 @@ unsafe impl SystemParamState for NonSendMutState { fn default_config() {} } -impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState { +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState { type Item = NonSendMut<'w, T>; #[inline] @@ -929,7 +937,8 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState { } } -/// The [`SystemParamState`] of `Option>`. +/// The [`SystemParamState`] of [`Option>`]. +/// See: [`NonSendMut`] pub struct OptionNonSendMutState(NonSendMutState); impl<'a, T: 'static> SystemParam for Option> { @@ -946,7 +955,7 @@ unsafe impl SystemParamState for OptionNonSendMutState { fn default_config() {} } -impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendMutState { +impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState { type Item = Option>; #[inline] @@ -991,7 +1000,7 @@ unsafe impl SystemParamState for ArchetypesState { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for ArchetypesState { +impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState { type Item = &'w Archetypes; #[inline] @@ -1026,7 +1035,7 @@ unsafe impl SystemParamState for ComponentsState { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for ComponentsState { +impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState { type Item = &'w Components; #[inline] @@ -1061,7 +1070,7 @@ unsafe impl SystemParamState for EntitiesState { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for EntitiesState { +impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState { type Item = &'w Entities; #[inline] @@ -1096,7 +1105,7 @@ unsafe impl SystemParamState for BundlesState { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for BundlesState { +impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState { type Item = &'w Bundles; #[inline] @@ -1123,7 +1132,7 @@ impl SystemParam for SystemChangeTick { type Fetch = SystemChangeTickState; } -/// The [`SystemParamState`] of [`SystemChangeTickState`]. +/// The [`SystemParamState`] of [`SystemChangeTick`]. pub struct SystemChangeTickState {} unsafe impl SystemParamState for SystemChangeTickState { @@ -1136,7 +1145,7 @@ unsafe impl SystemParamState for SystemChangeTickState { fn default_config() {} } -impl<'s, 'w> SystemParamFetch<'s, 'w> for SystemChangeTickState { +impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState { type Item = SystemChangeTick; unsafe fn get_param( @@ -1163,7 +1172,7 @@ macro_rules! impl_system_param_tuple { #[allow(unused_variables)] #[allow(non_snake_case)] - impl<'s, 'w, $($param: SystemParamFetch<'s, 'w>),*> SystemParamFetch<'s, 'w> for ($($param,)*) { + impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) { type Item = ($($param::Item,)*); #[inline] diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 11a0a8b842800..c2bae02ca01da 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1,5 +1,5 @@ use crate::{ - archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus}, + archetype::{Archetype, ArchetypeId, Archetypes}, bundle::{Bundle, BundleInfo}, change_detection::Ticks, component::{Component, ComponentId, ComponentTicks, Components, StorageType}, @@ -189,112 +189,29 @@ impl<'w> EntityMut<'w> { }) } - /// # Safety: - /// Partially moves the entity to a new archetype based on the provided bundle info - /// You must handle the other part of moving the entity yourself - unsafe fn get_insert_bundle_info<'a>( - entities: &mut Entities, - archetypes: &'a mut Archetypes, - components: &mut Components, - storages: &mut Storages, - bundle_info: &BundleInfo, - current_location: EntityLocation, - entity: Entity, - ) -> (&'a Archetype, &'a Vec, EntityLocation) { - // SAFE: component ids in `bundle_info` and self.location are valid - let new_archetype_id = add_bundle_to_archetype( - archetypes, - storages, - components, - current_location.archetype_id, - bundle_info, - ); - if new_archetype_id == current_location.archetype_id { - let archetype = &archetypes[current_location.archetype_id]; - let edge = archetype.edges().get_add_bundle(bundle_info.id).unwrap(); - (archetype, &edge.bundle_status, current_location) - } else { - let (old_table_row, old_table_id) = { - let old_archetype = &mut archetypes[current_location.archetype_id]; - let result = old_archetype.swap_remove(current_location.index); - if let Some(swapped_entity) = result.swapped_entity { - entities.meta[swapped_entity.id as usize].location = current_location; - } - (result.table_row, old_archetype.table_id()) - }; - - let new_table_id = archetypes[new_archetype_id].table_id(); - - let new_location = if old_table_id == new_table_id { - archetypes[new_archetype_id].allocate(entity, old_table_row) - } else { - let (old_table, new_table) = storages.tables.get_2_mut(old_table_id, new_table_id); - // PERF: store "non bundle" components in edge, then just move those to avoid - // redundant copies - let move_result = old_table.move_to_superset_unchecked(old_table_row, new_table); - - let new_location = - archetypes[new_archetype_id].allocate(entity, move_result.new_row); - // if an entity was moved into this entity's table spot, update its table row - if let Some(swapped_entity) = move_result.swapped_entity { - let swapped_location = entities.get(swapped_entity).unwrap(); - archetypes[swapped_location.archetype_id] - .set_entity_table_row(swapped_location.index, old_table_row); - } - new_location - }; - - entities.meta[entity.id as usize].location = new_location; - let (old_archetype, new_archetype) = - archetypes.get_2_mut(current_location.archetype_id, new_archetype_id); - let edge = old_archetype - .edges() - .get_add_bundle(bundle_info.id) - .unwrap(); - (&*new_archetype, &edge.bundle_status, new_location) - - // Sparse set components are intentionally ignored here. They don't need to move - } - } - - // TODO: move relevant methods to World (add/remove bundle) pub fn insert_bundle(&mut self, bundle: T) -> &mut Self { let change_tick = self.world.change_tick(); let bundle_info = self .world .bundles .init_info::(&mut self.world.components); - - let (archetype, bundle_status, new_location) = unsafe { - Self::get_insert_bundle_info( - &mut self.world.entities, - &mut self.world.archetypes, - &mut self.world.components, - &mut self.world.storages, - bundle_info, - self.location, - self.entity, - ) - }; - self.location = new_location; - - let table = &mut self.world.storages.tables[archetype.table_id()]; - let table_row = archetype.entity_table_row(new_location.index); - // SAFE: table row is valid + let mut bundle_inserter = bundle_info.get_bundle_inserter( + &mut self.world.entities, + &mut self.world.archetypes, + &mut self.world.components, + &mut self.world.storages, + self.location.archetype_id, + change_tick, + ); + // SAFE: location matches current entity. `T` matches `bundle_info` unsafe { - bundle_info.write_components( - &mut self.world.storages.sparse_sets, - self.entity, - table, - table_row, - bundle_status, - bundle, - change_tick, - ) - }; + self.location = bundle_inserter.insert(self.entity, self.location.index, bundle); + } + self } + // TODO: move to BundleInfo pub fn remove_bundle(&mut self) -> Option { let archetypes = &mut self.world.archetypes; let storages = &mut self.world.storages; @@ -340,6 +257,42 @@ impl<'w> EntityMut<'w> { }) }; + unsafe { + Self::move_entity_from_remove::( + entity, + &mut self.location, + old_location.archetype_id, + old_location, + entities, + archetypes, + storages, + new_archetype_id, + ); + } + + Some(result) + } + + /// Safety: `new_archetype_id` must have the same or a subset of the components + /// in `old_archetype_id`. Probably more safety stuff too, audit a call to + /// this fn as if the code here was written inline + /// + /// when DROP is true removed components will be dropped otherwise they will be forgotten + /// + // We use a const generic here so that we are less reliant on + // inlining for rustc to optimize out the `match DROP` + #[allow(clippy::too_many_arguments)] + unsafe fn move_entity_from_remove( + entity: Entity, + self_location: &mut EntityLocation, + old_archetype_id: ArchetypeId, + old_location: EntityLocation, + entities: &mut Entities, + archetypes: &mut Archetypes, + storages: &mut Storages, + new_archetype_id: ArchetypeId, + ) { + let old_archetype = &mut archetypes[old_archetype_id]; let remove_result = old_archetype.swap_remove(old_location.index); if let Some(swapped_entity) = remove_result.swapped_entity { entities.meta[swapped_entity.id as usize].location = old_location; @@ -349,36 +302,37 @@ impl<'w> EntityMut<'w> { let new_archetype = &mut archetypes[new_archetype_id]; let new_location = if old_table_id == new_archetype.table_id() { - unsafe { new_archetype.allocate(entity, old_table_row) } + new_archetype.allocate(entity, old_table_row) } else { let (old_table, new_table) = storages .tables .get_2_mut(old_table_id, new_archetype.table_id()); - // SAFE: table_row exists. All "missing" components have been extracted into the bundle - // above and the caller takes ownership - let move_result = - unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) }; + // SAFE: old_table_row exists + let move_result = if DROP { + old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) + } else { + old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) + }; - // SAFE: new_table_row is a valid position in new_archetype's table - let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; + // SAFE: move_result.new_row is a valid position in new_archetype's table + let new_location = new_archetype.allocate(entity, move_result.new_row); // if an entity was moved into this entity's table spot, update its table row if let Some(swapped_entity) = move_result.swapped_entity { let swapped_location = entities.get(swapped_entity).unwrap(); - let archetype = &mut archetypes[swapped_location.archetype_id]; - archetype.set_entity_table_row(swapped_location.index, old_table_row); + archetypes[swapped_location.archetype_id] + .set_entity_table_row(swapped_location.index, old_table_row); } new_location }; - self.location = new_location; - entities.meta[self.entity.id as usize].location = new_location; - - Some(result) + *self_location = new_location; + entities.meta[entity.id as usize].location = new_location; } + // TODO: move to BundleInfo /// Remove any components in the bundle that the entity has. pub fn remove_bundle_intersection(&mut self) { let archetypes = &mut self.world.archetypes; @@ -425,40 +379,18 @@ impl<'w> EntityMut<'w> { } } - let remove_result = old_archetype.swap_remove(old_location.index); - if let Some(swapped_entity) = remove_result.swapped_entity { - entities.meta[swapped_entity.id as usize].location = old_location; + unsafe { + Self::move_entity_from_remove::( + entity, + &mut self.location, + old_location.archetype_id, + old_location, + entities, + archetypes, + storages, + new_archetype_id, + ) } - let old_table_row = remove_result.table_row; - let old_table_id = old_archetype.table_id(); - let new_archetype = &mut archetypes[new_archetype_id]; - - let new_location = if old_table_id == new_archetype.table_id() { - unsafe { new_archetype.allocate(entity, old_table_row) } - } else { - let (old_table, new_table) = storages - .tables - .get_2_mut(old_table_id, new_archetype.table_id()); - - // SAFE: table_row exists - let move_result = - unsafe { old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table) }; - - // SAFE: new_table_row is a valid position in new_archetype's table - let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; - - // if an entity was moved into this entity's table spot, update its table row - if let Some(swapped_entity) = move_result.swapped_entity { - let swapped_location = entities.get(swapped_entity).unwrap(); - archetypes[swapped_location.archetype_id] - .set_entity_table_row(swapped_location.index, old_table_row); - } - - new_location - }; - - self.location = new_location; - entities.meta[self.entity.id as usize].location = new_location; } pub fn insert(&mut self, value: T) -> &mut Self { @@ -531,6 +463,7 @@ impl<'w> EntityMut<'w> { } } +// TODO: move to Storages? /// # Safety /// `entity_location` must be within bounds of the given archetype and `entity` must exist inside /// the archetype @@ -560,6 +493,7 @@ unsafe fn get_component( } } +// TODO: move to Storages? /// # Safety /// Caller must ensure that `component_id` is valid #[inline] @@ -590,6 +524,7 @@ unsafe fn get_component_and_ticks( } } +// TODO: move to Storages? /// Moves component data out of storage. /// /// This function leaves the underlying memory unchanged, but the component behind @@ -671,95 +606,6 @@ fn contains_component_with_id( world.archetypes[location.archetype_id].contains(component_id) } -/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same -/// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change. -/// Results are cached in the Archetype Graph to avoid redundant work. -/// -/// # Safety -/// components in `bundle_info` must exist -pub(crate) unsafe fn add_bundle_to_archetype( - archetypes: &mut Archetypes, - storages: &mut Storages, - components: &mut Components, - archetype_id: ArchetypeId, - bundle_info: &BundleInfo, -) -> ArchetypeId { - if let Some(add_bundle) = archetypes[archetype_id] - .edges() - .get_add_bundle(bundle_info.id) - { - return add_bundle.archetype_id; - } - let mut new_table_components = Vec::new(); - let mut new_sparse_set_components = Vec::new(); - let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len()); - - let current_archetype = &mut archetypes[archetype_id]; - for component_id in bundle_info.component_ids.iter().cloned() { - if current_archetype.contains(component_id) { - bundle_status.push(ComponentStatus::Mutated); - } else { - bundle_status.push(ComponentStatus::Added); - let component_info = components.get_info_unchecked(component_id); - match component_info.storage_type() { - StorageType::Table => new_table_components.push(component_id), - StorageType::SparseSet => { - storages.sparse_sets.get_or_insert(component_info); - new_sparse_set_components.push(component_id) - } - } - } - } - - if new_table_components.is_empty() && new_sparse_set_components.is_empty() { - let edges = current_archetype.edges_mut(); - // the archetype does not change when we add this bundle - edges.set_add_bundle(bundle_info.id, archetype_id, bundle_status); - archetype_id - } else { - let table_id; - let table_components; - let sparse_set_components; - // the archetype changes when we add this bundle. prepare the new archetype and storages - { - let current_archetype = &archetypes[archetype_id]; - table_components = if new_table_components.is_empty() { - // if there are no new table components, we can keep using this table - table_id = current_archetype.table_id(); - current_archetype.table_components().to_vec() - } else { - new_table_components.extend(current_archetype.table_components()); - // sort to ignore order while hashing - new_table_components.sort(); - // SAFE: all component ids in `new_table_components` exist - table_id = storages - .tables - .get_id_or_insert(&new_table_components, components); - - new_table_components - }; - - sparse_set_components = if new_sparse_set_components.is_empty() { - current_archetype.sparse_set_components().to_vec() - } else { - new_sparse_set_components.extend(current_archetype.sparse_set_components()); - // sort to ignore order while hashing - new_sparse_set_components.sort(); - new_sparse_set_components - }; - }; - let new_archetype_id = - archetypes.get_id_or_insert(table_id, table_components, sparse_set_components); - // add an edge from the old archetype to the new archetype - archetypes[archetype_id].edges_mut().set_add_bundle( - bundle_info.id, - new_archetype_id, - bundle_status, - ); - new_archetype_id - } -} - /// Removes a bundle from the given archetype and returns the resulting archetype (or None if the /// removal was invalid). in the event that adding the given bundle does not result in an Archetype /// change. Results are cached in the Archetype Graph to avoid redundant work. @@ -814,7 +660,7 @@ unsafe fn remove_bundle_from_archetype( // graph current_archetype .edges_mut() - .set_remove_bundle(bundle_info.id, None); + .insert_remove_bundle(bundle_info.id, None); return None; } } @@ -853,11 +699,11 @@ unsafe fn remove_bundle_from_archetype( if intersection { current_archetype .edges_mut() - .set_remove_bundle_intersection(bundle_info.id, result); + .insert_remove_bundle_intersection(bundle_info.id, result); } else { current_archetype .edges_mut() - .set_remove_bundle(bundle_info.id, result); + .insert_remove_bundle(bundle_info.id, result); } result } diff --git a/crates/bevy_ecs/src/world/mod.rs b/crates/bevy_ecs/src/world/mod.rs index e39d1b488630b..f4a542b7f13a6 100644 --- a/crates/bevy_ecs/src/world/mod.rs +++ b/crates/bevy_ecs/src/world/mod.rs @@ -1,16 +1,15 @@ mod entity_ref; -mod pointer; mod spawn_batch; mod world_cell; +pub use crate::change_detection::Mut; pub use entity_ref::*; -pub use pointer::*; pub use spawn_batch::*; pub use world_cell::*; use crate::{ archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes}, - bundle::{Bundle, Bundles}, + bundle::{Bundle, BundleInserter, BundleSpawner, Bundles}, change_detection::Ticks, component::{ Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError, @@ -221,8 +220,13 @@ impl World { self.get_entity_mut(entity).expect("Entity does not exist") } - /// Returns an EntityMut for an existing entity or creates one if it doesn't exist. - /// This will return `None` if the entity exists with a different generation. + /// Returns an [EntityMut] for the given `entity` (if it exists) or spawns one if it doesn't exist. + /// This will return [None] if the `entity` exists with a different generation. + /// + /// # Note + /// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`World::spawn`]. + /// This method should generally only be used for sharing entities across apps, and only when they have a + /// scheme worked out to share an ID space (which doesn't happen by default). #[inline] pub fn get_or_spawn(&mut self, entity: Entity) -> Option { self.flush(); @@ -379,6 +383,7 @@ impl World { /// .id(); /// let position = world.get::(entity).unwrap(); /// assert_eq!(position.x, 0.0); + /// ``` #[inline] pub fn get(&self, entity: Entity) -> Option<&T> { self.get_entity(entity)?.get() @@ -400,6 +405,7 @@ impl World { /// .id(); /// let mut position = world.get_mut::(entity).unwrap(); /// position.x = 1.0; + /// ``` #[inline] pub fn get_mut(&mut self, entity: Entity) -> Option> { self.get_entity_mut(entity)?.get_mut() @@ -699,6 +705,127 @@ impl World { self.get_non_send_unchecked_mut_with_id(component_id) } + /// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given + /// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists). + /// This is faster than doing equivalent operations one-by-one. + /// Returns [Ok] if all entities were successfully inserted into or spawned. Otherwise it returns an [Err] + /// with a list of entities that could not be spawned or inserted into. A "spawn or insert" operation can + /// only fail if an [Entity] is passed in with an "invalid generation" that conflicts with an existing [Entity]. + /// + /// # Note + /// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`World::spawn_batch`]. + /// This method should generally only be used for sharing entities across apps, and only when they have a scheme + /// worked out to share an ID space (which doesn't happen by default). + /// + /// ``` + /// use bevy_ecs::{entity::Entity, world::World}; + /// + /// let mut world = World::new(); + /// let e0 = world.spawn().id(); + /// let e1 = world.spawn().id(); + /// world.insert_or_spawn_batch(vec![ + /// (e0, ("a", 0.0)), // the first entity + /// (e1, ("b", 1.0)), // the second entity + /// ]); + /// + /// assert_eq!(world.get::(e0), Some(&0.0)); + /// ``` + pub fn insert_or_spawn_batch(&mut self, iter: I) -> Result<(), Vec> + where + I: IntoIterator, + I::IntoIter: Iterator, + B: Bundle, + { + self.flush(); + + let iter = iter.into_iter(); + let change_tick = *self.change_tick.get_mut(); + + let bundle_info = self.bundles.init_info::(&mut self.components); + enum SpawnOrInsert<'a, 'b> { + Spawn(BundleSpawner<'a, 'b>), + Insert(BundleInserter<'a, 'b>, ArchetypeId), + } + + impl<'a, 'b> SpawnOrInsert<'a, 'b> { + fn entities(&mut self) -> &mut Entities { + match self { + SpawnOrInsert::Spawn(spawner) => spawner.entities, + SpawnOrInsert::Insert(inserter, _) => inserter.entities, + } + } + } + let mut spawn_or_insert = SpawnOrInsert::Spawn(bundle_info.get_bundle_spawner( + &mut self.entities, + &mut self.archetypes, + &mut self.components, + &mut self.storages, + change_tick, + )); + + let mut invalid_entities = Vec::new(); + for (entity, bundle) in iter { + match spawn_or_insert + .entities() + .alloc_at_without_replacement(entity) + { + AllocAtWithoutReplacement::Exists(location) => { + match spawn_or_insert { + SpawnOrInsert::Insert(ref mut inserter, archetype) + if location.archetype_id == archetype => + { + // SAFE: `entity` is valid, `location` matches entity, bundle matches inserter + unsafe { inserter.insert(entity, location.index, bundle) }; + } + _ => { + let mut inserter = bundle_info.get_bundle_inserter( + &mut self.entities, + &mut self.archetypes, + &mut self.components, + &mut self.storages, + location.archetype_id, + change_tick, + ); + // SAFE: `entity` is valid, `location` matches entity, bundle matches inserter + unsafe { inserter.insert(entity, location.index, bundle) }; + spawn_or_insert = + SpawnOrInsert::Insert(inserter, location.archetype_id); + } + }; + } + AllocAtWithoutReplacement::DidNotExist => { + match spawn_or_insert { + SpawnOrInsert::Spawn(ref mut spawner) => { + // SAFE: `entity` is allocated (but non existent), bundle matches inserter + unsafe { spawner.spawn_non_existent(entity, bundle) }; + } + _ => { + let mut spawner = bundle_info.get_bundle_spawner( + &mut self.entities, + &mut self.archetypes, + &mut self.components, + &mut self.storages, + change_tick, + ); + // SAFE: `entity` is valid, `location` matches entity, bundle matches inserter + unsafe { spawner.spawn_non_existent(entity, bundle) }; + spawn_or_insert = SpawnOrInsert::Spawn(spawner); + } + }; + } + AllocAtWithoutReplacement::ExistsWithWrongGeneration => { + invalid_entities.push(entity); + } + } + } + + if invalid_entities.is_empty() { + Ok(()) + } else { + Err(invalid_entities) + } + } + /// Temporarily removes the requested resource from this [World], then re-adds it before /// returning. This enables safe mutable access to a resource while still providing mutable /// world access @@ -838,7 +965,7 @@ impl World { let resource_archetype = self .archetypes .archetypes - .get_unchecked_mut(ArchetypeId::resource().index()); + .get_unchecked_mut(ArchetypeId::RESOURCE.index()); let resource_archetype_components = &mut resource_archetype.components; let archetype_component_count = &mut self.archetypes.archetype_component_count; let components = &self.components; diff --git a/crates/bevy_ecs/src/world/pointer.rs b/crates/bevy_ecs/src/world/pointer.rs deleted file mode 100644 index f60995a43c3dc..0000000000000 --- a/crates/bevy_ecs/src/world/pointer.rs +++ /dev/null @@ -1 +0,0 @@ -pub use crate::change_detection::Mut; diff --git a/crates/bevy_ecs/src/world/spawn_batch.rs b/crates/bevy_ecs/src/world/spawn_batch.rs index 95830dc72b696..6f84557684633 100644 --- a/crates/bevy_ecs/src/world/spawn_batch.rs +++ b/crates/bevy_ecs/src/world/spawn_batch.rs @@ -1,9 +1,7 @@ use crate::{ - archetype::{Archetype, ArchetypeId, ComponentStatus}, - bundle::{Bundle, BundleInfo}, - entity::{Entities, Entity}, - storage::{SparseSets, Table}, - world::{add_bundle_to_archetype, World}, + bundle::{Bundle, BundleSpawner}, + entity::Entity, + world::World, }; pub struct SpawnBatchIter<'w, I> @@ -12,13 +10,7 @@ where I::Item: Bundle, { inner: I, - entities: &'w mut Entities, - archetype: &'w mut Archetype, - table: &'w mut Table, - sparse_sets: &'w mut SparseSets, - bundle_info: &'w BundleInfo, - bundle_status: &'w [ComponentStatus], - change_tick: u32, + spawner: BundleSpawner<'w, 'w>, } impl<'w, I> SpawnBatchIter<'w, I> @@ -33,40 +25,22 @@ where world.flush(); let (lower, upper) = iter.size_hint(); + let length = upper.unwrap_or(lower); let bundle_info = world.bundles.init_info::(&mut world.components); - - let length = upper.unwrap_or(lower); - // SAFE: empty archetype exists and bundle components were initialized above - let archetype_id = unsafe { - add_bundle_to_archetype( - &mut world.archetypes, - &mut world.storages, - &mut world.components, - ArchetypeId::empty(), - bundle_info, - ) - }; - let (empty_archetype, archetype) = world - .archetypes - .get_2_mut(ArchetypeId::empty(), archetype_id); - let table = &mut world.storages.tables[archetype.table_id()]; - archetype.reserve(length); - table.reserve(length); world.entities.reserve(length as u32); - let edge = empty_archetype - .edges() - .get_add_bundle(bundle_info.id()) - .unwrap(); + let mut spawner = bundle_info.get_bundle_spawner( + &mut world.entities, + &mut world.archetypes, + &mut world.components, + &mut world.storages, + *world.change_tick.get_mut(), + ); + spawner.reserve_storage(length); + Self { inner: iter, - entities: &mut world.entities, - archetype, - table, - sparse_sets: &mut world.storages.sparse_sets, - bundle_info, - change_tick: *world.change_tick.get_mut(), - bundle_status: &edge.bundle_status, + spawner, } } } @@ -90,24 +64,8 @@ where fn next(&mut self) -> Option { let bundle = self.inner.next()?; - let entity = self.entities.alloc(); - // SAFE: component values are immediately written to relevant storages (which have been - // allocated) - unsafe { - let table_row = self.table.allocate(entity); - let location = self.archetype.allocate(entity, table_row); - self.bundle_info.write_components( - self.sparse_sets, - entity, - self.table, - table_row, - self.bundle_status, - bundle, - self.change_tick, - ); - self.entities.meta[entity.id as usize].location = location; - } - Some(entity) + // SAFE: bundle matches spawner type + unsafe { Some(self.spawner.spawn(bundle)) } } fn size_hint(&self) -> (usize, Option) { diff --git a/crates/bevy_ecs/src/world/world_cell.rs b/crates/bevy_ecs/src/world/world_cell.rs index e128167d5610d..8ac21ac32654b 100644 --- a/crates/bevy_ecs/src/world/world_cell.rs +++ b/crates/bevy_ecs/src/world/world_cell.rs @@ -296,7 +296,7 @@ mod tests { .components .get_resource_id(TypeId::of::()) .unwrap(); - let resource_archetype = world.archetypes.get(ArchetypeId::resource()).unwrap(); + let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap(); let u32_archetype_component_id = resource_archetype .get_archetype_component_id(u32_component_id) .unwrap(); diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 7c82d528ea540..c18b6ed307990 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -2,10 +2,6 @@ name = "bevy_render" version = "0.5.0" edition = "2018" -authors = [ - "Bevy Contributors ", - "Carter Anderson ", -] description = "Provides rendering functionality for Bevy Engine" homepage = "https://bevyengine.org" repository = "https://github.com/bevyengine/bevy" 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/color.rs b/crates/bevy_render/src/color.rs index bb8b1f80c4702..5c12053d3e4db 100644 --- a/crates/bevy_render/src/color.rs +++ b/crates/bevy_render/src/color.rs @@ -10,6 +10,7 @@ use bevy_math::{Vec3, Vec4}; use bevy_reflect::{Reflect, ReflectDeserialize}; use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign, Mul, MulAssign}; +use thiserror::Error; #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] #[reflect(PartialEq, Serialize, Deserialize)] @@ -1070,10 +1071,12 @@ impl Bytes for Color { impl_render_resource_bytes!(Color); -#[derive(Debug)] +#[derive(Debug, Error)] pub enum HexColorError { + #[error("Unexpected length of hex string")] Length, - Hex(hex::FromHexError), + #[error("Error parsing hex value")] + Hex(#[from] hex::FromHexError), } fn decode_rgb(data: &[u8]) -> Result { diff --git a/crates/bevy_render/src/draw.rs b/crates/bevy_render/src/draw.rs index 13a18fa922ac3..9d4b6ce54f834 100644 --- a/crates/bevy_render/src/draw.rs +++ b/crates/bevy_render/src/draw.rs @@ -164,7 +164,7 @@ pub enum DrawError { } #[derive(SystemParam)] -pub struct DrawContext<'s, 'w> { +pub struct DrawContext<'w, 's> { pub pipelines: ResMut<'w, Assets>, pub shaders: ResMut<'w, Assets>, pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>, @@ -177,7 +177,7 @@ pub struct DrawContext<'s, 'w> { marker: PhantomData<&'s usize>, } -impl<'s, 'w> DrawContext<'s, 'w> { +impl<'w, 's> DrawContext<'w, 's> { pub fn get_uniform_buffer( &mut self, render_resource: &T, diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 2ff192eeb69fb..e26be98280c1c 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -14,7 +14,7 @@ pub mod wireframe; use bevy_ecs::{ schedule::{ParallelSystemDescriptorCoercion, SystemStage}, - system::{IntoExclusiveSystem, IntoSystem, Res}, + system::{IntoExclusiveSystem, Res}, }; use bevy_transform::TransformSystem; use bevy_utils::tracing::warn; @@ -174,58 +174,38 @@ impl Plugin for RenderPlugin { .init_resource::() .init_resource::() .init_resource::() - .add_startup_system_to_stage( - StartupStage::PreStartup, - check_for_render_resource_context.system(), - ) - .add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system.system()) - .add_system_to_stage( - CoreStage::PostUpdate, - camera::active_cameras_system.system(), - ) + .add_startup_system_to_stage(StartupStage::PreStartup, check_for_render_resource_context) + .add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system) + .add_system_to_stage(CoreStage::PostUpdate, camera::active_cameras_system) .add_system_to_stage( CoreStage::PostUpdate, - camera::camera_system:: - .system() - .before(RenderSystem::VisibleEntities), + camera::camera_system::.before(RenderSystem::VisibleEntities), ) .add_system_to_stage( CoreStage::PostUpdate, - camera::camera_system:: - .system() - .before(RenderSystem::VisibleEntities), + camera::camera_system::.before(RenderSystem::VisibleEntities), ) .add_system_to_stage( CoreStage::PostUpdate, camera::visible_entities_system - .system() .label(RenderSystem::VisibleEntities) .after(TransformSystem::TransformPropagate), ) + .add_system_to_stage(RenderStage::RenderResource, shader::shader_update_system) .add_system_to_stage( RenderStage::RenderResource, - shader::shader_update_system.system(), - ) - .add_system_to_stage( - RenderStage::RenderResource, - mesh::mesh_resource_provider_system.system(), + mesh::mesh_resource_provider_system, ) .add_system_to_stage( RenderStage::RenderResource, - Texture::texture_resource_system.system(), + Texture::texture_resource_system, ) .add_system_to_stage( RenderStage::RenderGraphSystems, render_graph::render_graph_schedule_executor_system.exclusive_system(), ) - .add_system_to_stage( - RenderStage::Draw, - pipeline::draw_render_pipelines_system.system(), - ) - .add_system_to_stage( - RenderStage::PostRender, - shader::clear_shader_defs_system.system(), - ); + .add_system_to_stage(RenderStage::Draw, pipeline::draw_render_pipelines_system) + .add_system_to_stage(RenderStage::PostRender, shader::clear_shader_defs_system); if let Some(ref config) = self.base_render_graph_config { crate::base::add_base_graph(config, &mut app.world); 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/camera_node.rs b/crates/bevy_render/src/render_graph/nodes/camera_node.rs index c2cc678863458..3d6f72e452efe 100644 --- a/crates/bevy_render/src/render_graph/nodes/camera_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/camera_node.rs @@ -8,7 +8,7 @@ use crate::{ }; use bevy_core::bytes_of; use bevy_ecs::{ - system::{BoxedSystem, IntoSystem, Local, Query, Res, ResMut}, + system::{BoxedSystem, ConfigurableSystem, Local, Query, Res, ResMut}, world::World, }; use bevy_transform::prelude::*; @@ -46,7 +46,7 @@ impl Node for CameraNode { impl SystemNode for CameraNode { fn get_system(&self) -> BoxedSystem { - let system = camera_node_system.system().config(|config| { + let system = camera_node_system.config(|config| { config.0 = Some(CameraNodeState { camera_name: self.camera_name.clone(), command_queue: self.command_queue.clone(), 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 2b46a9a6b715d..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,8 +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, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res, ResMut}, + system::{BoxedSystem, ConfigurableSystem, Local, QuerySet, RemovedComponents, Res, ResMut}, world::World, }; use bevy_utils::HashMap; @@ -400,7 +401,7 @@ where T: renderer::RenderResources, { fn get_system(&self) -> BoxedSystem { - let system = render_resources_node_system::.system().config(|config| { + let system = render_resources_node_system::.config(|config| { config.0 = Some(RenderResourcesNodeState { command_queue: self.command_queue.clone(), uniform_buffer_arrays: UniformBufferArrays::::default(), @@ -435,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(); @@ -444,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); } @@ -454,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, @@ -467,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; } @@ -496,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; @@ -513,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; @@ -583,15 +584,13 @@ where T: renderer::RenderResources + Asset, { fn get_system(&self) -> BoxedSystem { - let system = asset_render_resources_node_system:: - .system() - .config(|config| { - config.0 = Some(RenderResourcesNodeState { - command_queue: self.command_queue.clone(), - uniform_buffer_arrays: UniformBufferArrays::::default(), - dynamic_uniforms: self.dynamic_uniforms, - }) - }); + let system = asset_render_resources_node_system::.config(|config| { + config.0 = Some(RenderResourcesNodeState { + command_queue: self.command_queue.clone(), + uniform_buffer_arrays: UniformBufferArrays::::default(), + dynamic_uniforms: self.dynamic_uniforms, + }) + }); Box::new(system) } @@ -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/shader/shader_reflect.rs b/crates/bevy_render/src/shader/shader_reflect.rs index 9952970e8f4c0..9210c41ef8325 100644 --- a/crates/bevy_render/src/shader/shader_reflect.rs +++ b/crates/bevy_render/src/shader/shader_reflect.rs @@ -151,7 +151,19 @@ fn reflect_binding( filtering: true, }, ), - _ => panic!("Unsupported bind type {:?}.", binding.descriptor_type), + _ => { + let ReflectDescriptorBinding { + descriptor_type, + name, + set, + binding, + .. + } = binding; + panic!( + "Unsupported shader bind type {:?} (name '{}', set {}, binding {})", + descriptor_type, name, set, binding + ); + } }; let shader_stage = match shader_stage { diff --git a/crates/bevy_render/src/wireframe/mod.rs b/crates/bevy_render/src/wireframe/mod.rs index c6b918540d4b1..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::{IntoSystem, Query, QuerySet, Res}, + system::{QuerySet, Res}, world::Mut, }; use bevy_reflect::{Reflect, TypeUuid}; @@ -27,7 +27,7 @@ pub struct WireframePlugin; impl Plugin for WireframePlugin { fn build(&self, app: &mut App) { app.init_resource::() - .add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system.system()); + .add_system_to_stage(crate::RenderStage::Draw, draw_wireframes_system); let world = app.world.cell(); let mut shaders = world.get_resource_mut::>().unwrap(); let mut pipelines = world @@ -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/Cargo.toml b/crates/bevy_text/Cargo.toml index 1d9ec7765557c..f4be2c9a855db 100644 --- a/crates/bevy_text/Cargo.toml +++ b/crates/bevy_text/Cargo.toml @@ -2,10 +2,6 @@ name = "bevy_text" version = "0.5.0" edition = "2018" -authors = [ - "Bevy Contributors ", - "Carter Anderson ", -] description = "Provides text functionality for Bevy Engine" homepage = "https://bevyengine.org" repository = "https://github.com/bevyengine/bevy" diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index f69ffb4a29e9a..ce91d4a2e8ba3 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -29,7 +29,7 @@ pub mod prelude { use bevy_app::prelude::*; use bevy_asset::AddAsset; -use bevy_ecs::{entity::Entity, system::IntoSystem}; +use bevy_ecs::entity::Entity; use bevy_render::RenderStage; pub type DefaultTextPipeline = TextPipeline; @@ -43,7 +43,7 @@ impl Plugin for TextPlugin { .add_asset::() .init_asset_loader::() .insert_resource(DefaultTextPipeline::default()) - .add_system_to_stage(CoreStage::PostUpdate, text2d_system.system()) - .add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system.system()); + .add_system_to_stage(CoreStage::PostUpdate, text2d_system) + .add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system); } } 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/Cargo.toml b/crates/bevy_ui/Cargo.toml index 0735b48971b52..07e653d83447d 100644 --- a/crates/bevy_ui/Cargo.toml +++ b/crates/bevy_ui/Cargo.toml @@ -2,10 +2,6 @@ name = "bevy_ui" version = "0.5.0" edition = "2018" -authors = [ - "Bevy Contributors ", - "Carter Anderson ", -] description = "A custom ECS-driven UI framework built specifically for Bevy Engine" homepage = "https://bevyengine.org" repository = "https://github.com/bevyengine/bevy" diff --git a/crates/bevy_ui/src/flex/convert.rs b/crates/bevy_ui/src/flex/convert.rs index 4335ca4306b2b..10e6839134349 100644 --- a/crates/bevy_ui/src/flex/convert.rs +++ b/crates/bevy_ui/src/flex/convert.rs @@ -114,8 +114,8 @@ impl From for stretch::style::Direction { fn from(value: Direction) -> Self { match value { Direction::Inherit => stretch::style::Direction::Inherit, - Direction::Ltr => stretch::style::Direction::LTR, - Direction::Rtl => stretch::style::Direction::RTL, + Direction::LeftToRight => stretch::style::Direction::LTR, + Direction::RightToLeft => stretch::style::Direction::RTL, } } } diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index 862aa66e172bb..e9ca5b3b328f8 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -22,10 +22,7 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::{ - schedule::{ParallelSystemDescriptorCoercion, SystemLabel}, - system::IntoSystem, -}; +use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel}; use bevy_input::InputSystem; use bevy_math::{Rect, Size}; use bevy_render::RenderStage; @@ -62,35 +59,30 @@ impl Plugin for UiPlugin { .register_type::() .add_system_to_stage( CoreStage::PreUpdate, - ui_focus_system - .system() - .label(UiSystem::Focus) - .after(InputSystem), + ui_focus_system.label(UiSystem::Focus).after(InputSystem), ) // add these stages to front because these must run before transform update systems .add_system_to_stage( CoreStage::PostUpdate, - widget::text_system.system().before(UiSystem::Flex), + widget::text_system.before(UiSystem::Flex), ) .add_system_to_stage( CoreStage::PostUpdate, - widget::image_node_system.system().before(UiSystem::Flex), + widget::image_node_system.before(UiSystem::Flex), ) .add_system_to_stage( CoreStage::PostUpdate, flex_node_system - .system() .label(UiSystem::Flex) .before(TransformSystem::TransformPropagate), ) .add_system_to_stage( CoreStage::PostUpdate, ui_z_system - .system() .after(UiSystem::Flex) .before(TransformSystem::TransformPropagate), ) - .add_system_to_stage(RenderStage::Draw, widget::draw_text_system.system()); + .add_system_to_stage(RenderStage::Draw, widget::draw_text_system); crate::render::add_ui_graph(&mut app.world); } diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 576fb8833d468..9b8dd54da3872 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -155,8 +155,8 @@ impl Default for AlignContent { #[reflect_value(PartialEq, Serialize, Deserialize)] pub enum Direction { Inherit, - Ltr, - Rtl, + LeftToRight, + RightToLeft, } impl Default for Direction { diff --git a/crates/bevy_ui/src/update.rs b/crates/bevy_ui/src/update.rs index fd3fe3690e99c..42876a7c30c07 100644 --- a/crates/bevy_ui/src/update.rs +++ b/crates/bevy_ui/src/update.rs @@ -54,7 +54,7 @@ fn update_hierarchy( mod tests { use bevy_ecs::{ schedule::{Schedule, Stage, SystemStage}, - system::{CommandQueue, Commands, IntoSystem}, + system::{CommandQueue, Commands}, world::World, }; use bevy_transform::{components::Transform, hierarchy::BuildChildren}; @@ -122,7 +122,7 @@ mod tests { let mut schedule = Schedule::default(); let mut update_stage = SystemStage::parallel(); - update_stage.add_system(ui_z_system.system()); + update_stage.add_system(ui_z_system); schedule.add_stage("update", update_stage); schedule.run(&mut world); 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