Add upstream bevy_ecs and prepare for custom-shaders merge (#2815)

This updates the `pipelined-rendering` branch to use the latest `bevy_ecs` from `main`. This accomplishes a couple of goals:

1. prepares for upcoming `custom-shaders` branch changes, which were what drove many of the recent bevy_ecs changes on `main`
2. prepares for the soon-to-happen merge of `pipelined-rendering` into `main`. By including bevy_ecs changes now, we make that merge simpler / easier to review. 

I split this up into 3 commits:

1. **add upstream bevy_ecs**: please don't bother reviewing this content. it has already received thorough review on `main` and is a literal copy/paste of the relevant folders (the old folders were deleted so the directories are literally exactly the same as `main`).
2. **support manual buffer application in stages**: this is used to enable the Extract step. we've already reviewed this once on the `pipelined-rendering` branch, but its worth looking at one more time in the new context of (1).
3. **support manual archetype updates in QueryState**: same situation as (2).
This commit is contained in:
Carter Anderson 2021-09-14 06:14:19 +00:00
parent 59bfbd3295
commit 11b41206eb
147 changed files with 2321 additions and 1767 deletions

View file

@ -365,10 +365,6 @@ path = "examples/ecs/system_sets.rs"
name = "timers" name = "timers"
path = "examples/ecs/timers.rs" path = "examples/ecs/timers.rs"
[[example]]
name = "query_bundle"
path = "examples/ecs/query_bundle.rs"
# Games # Games
[[example]] [[example]]
name = "alien_cake_addict" name = "alien_cake_addict"

View file

@ -2,10 +2,6 @@
name = "bevy_ecs" name = "bevy_ecs"
version = "0.5.0" version = "0.5.0"
edition = "2018" edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "Bevy Engine's entity component system" description = "Bevy Engine's entity component system"
homepage = "https://bevyengine.org" homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy" repository = "https://github.com/bevyengine/bevy"

View file

@ -131,7 +131,7 @@ fn main() {
// Add a Stage to our schedule. Each Stage in a schedule runs all of its systems // Add a Stage to our schedule. Each Stage in a schedule runs all of its systems
// before moving on to the next Stage // before moving on to the next Stage
schedule.add_stage("update", SystemStage::parallel() 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 // Run the schedule once. If your app has a "loop", you would run this once per loop

View file

@ -24,15 +24,11 @@ fn main() {
// Add systems to the Stage to execute our app logic // 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 // 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(spawn_entities.label(SimulationSystem::Spawn));
update.add_system( update.add_system(print_counter_when_changed.after(SimulationSystem::Spawn));
print_counter_when_changed update.add_system(age_all_entities.label(SimulationSystem::Age));
.system() update.add_system(remove_old_entities.after(SimulationSystem::Age));
.after(SimulationSystem::Spawn), update.add_system(print_changed_entities.after(SimulationSystem::Age));
);
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));
// Add the Stage with our systems to the Schedule // Add the Stage with our systems to the Schedule
schedule.add_stage("update", update); schedule.add_stage("update", update);

View file

@ -22,7 +22,7 @@ fn main() {
// Setup a schedule and stage to add a system querying for the just spawned entities // Setup a schedule and stage to add a system querying for the just spawned entities
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
let mut update = SystemStage::parallel(); let mut update = SystemStage::parallel();
update.add_system(query_entities.system()); update.add_system(query_entities);
schedule.add_stage("update", update); schedule.add_stage("update", update);
schedule.run(&mut world); schedule.run(&mut world);

View file

@ -16,13 +16,13 @@ fn main() {
// called "second". In "first" we update the events and in "second" we run our systems // called "second". In "first" we update the events and in "second" we run our systems
// sending and receiving events. // sending and receiving events.
let mut first = SystemStage::parallel(); let mut first = SystemStage::parallel();
first.add_system(Events::<MyEvent>::update_system.system()); first.add_system(Events::<MyEvent>::update_system);
schedule.add_stage("first", first); schedule.add_stage("first", first);
// Add systems sending and receiving events to a "second" Stage // Add systems sending and receiving events to a "second" Stage
let mut second = SystemStage::parallel(); let mut second = SystemStage::parallel();
second.add_system(sending_system.system().label(EventSystem::Sending)); second.add_system(sending_system.label(EventSystem::Sending));
second.add_system(receiving_system.system().after(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 // 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); schedule.add_stage_after("first", "second", second);
@ -41,7 +41,6 @@ enum EventSystem {
} }
// This is our event that we will send and receive in systems // This is our event that we will send and receive in systems
#[derive(Debug)]
struct MyEvent { struct MyEvent {
pub message: String, pub message: String,
pub random_value: f32, pub random_value: f32,
@ -62,6 +61,9 @@ fn sending_system(mut event_writer: EventWriter<MyEvent>) {
// If an event is received it will be printed to the console // If an event is received it will be printed to the console
fn receiving_system(mut event_reader: EventReader<MyEvent>) { fn receiving_system(mut event_reader: EventReader<MyEvent>) {
for my_event in event_reader.iter() { for my_event in event_reader.iter() {
println!(" Received: {:?}", *my_event); println!(
" Received message {:?}, with random value of {}",
my_event.message, my_event.random_value
);
} }
} }

View file

@ -16,8 +16,8 @@ fn main() {
let mut update = SystemStage::parallel(); let mut update = SystemStage::parallel();
// Add systems to increase the counter and to print out the current value // 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(increase_counter.label(CounterSystem::Increase));
update.add_system(print_counter.system().after(CounterSystem::Increase)); update.add_system(print_counter.after(CounterSystem::Increase));
schedule.add_stage("update", update); schedule.add_stage("update", update);
for iteration in 1..=10 { for iteration in 1..=10 {

View file

@ -1,16 +1,16 @@
extern crate proc_macro; extern crate proc_macro;
use bevy_macro_utils::{derive_label, BevyManifest}; use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Span; use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
parse_macro_input, parse_macro_input,
punctuated::Punctuated, punctuated::Punctuated,
token::Comma, token::Comma,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt, Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result,
Result, Token, Token,
}; };
struct AllTuples { struct AllTuples {
@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
.map(|field| &field.ty) .map(|field| &field.ty)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut field_type_infos = Vec::new(); let mut field_component_ids = Vec::new();
let mut field_get_components = Vec::new(); let mut field_get_components = Vec::new();
let mut field_from_components = Vec::new(); let mut field_from_components = Vec::new();
for ((field_type, is_bundle), field) in for ((field_type, is_bundle), field) in
field_type.iter().zip(is_bundle.iter()).zip(field.iter()) field_type.iter().zip(is_bundle.iter()).zip(field.iter())
{ {
if *is_bundle { if *is_bundle {
field_type_infos.push(quote! { field_component_ids.push(quote! {
type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info()); component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components));
}); });
field_get_components.push(quote! { field_get_components.push(quote! {
self.#field.get_components(&mut func); 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), #field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
}); });
} else { } else {
field_type_infos.push(quote! { field_component_ids.push(quote! {
type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>()); component_ids.push(components.get_or_insert_id::<#field_type>());
}); });
field_get_components.push(quote! { field_get_components.push(quote! {
func((&mut self.#field as *mut #field_type).cast::<u8>()); func((&mut self.#field as *mut #field_type).cast::<u8>());
@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
TokenStream::from(quote! { TokenStream::from(quote! {
/// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order /// 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 { unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
fn type_info() -> Vec<#ecs_path::component::TypeInfo> { fn component_ids(
let mut type_info = Vec::with_capacity(#field_len); components: &mut #ecs_path::component::Components,
#(#field_type_infos)* ) -> Vec<#ecs_path::component::ComponentId> {
type_info let mut component_ids = Vec::with_capacity(#field_len);
#(#field_component_ids)*
component_ids
} }
#[allow(unused_variables, unused_mut, non_snake_case)] #[allow(unused_variables, unused_mut, non_snake_case)]
@ -174,38 +176,26 @@ fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<Ident> {
.collect::<Vec<Ident>>() .collect::<Vec<Ident>>()
} }
fn get_lifetimes(fmt_string: fn(usize) -> String, count: usize) -> Vec<Lifetime> {
(0..count)
.map(|i| Lifetime::new(&fmt_string(i), Span::call_site()))
.collect::<Vec<Lifetime>>()
}
#[proc_macro] #[proc_macro]
pub fn impl_query_set(_input: TokenStream) -> TokenStream { pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let mut tokens = TokenStream::new(); let mut tokens = TokenStream::new();
let max_queries = 4; let max_queries = 4;
let queries = get_idents(|i| format!("Q{}", i), max_queries); let queries = get_idents(|i| format!("Q{}", i), max_queries);
let filters = get_idents(|i| format!("F{}", 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(); let mut query_fn_muts = Vec::new();
for i in 0..max_queries { for i in 0..max_queries {
let query = &queries[i]; let query = &queries[i];
let filter = &filters[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 = 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); 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! { query_fn_muts.push(quote! {
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> { pub fn #fn_name(&mut self) -> Query<'_, '_, #query, #filter> {
&mut self.0.#index // 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 { for query_count in 1..=max_queries {
let query = &queries[0..query_count]; let query = &queries[0..query_count];
let filter = &filters[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]; let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! { 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,)* where #(#filter::Fetch: FilterFetch,)*
{ {
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>; type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
@ -271,10 +258,10 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
fn default_config() {} 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,)* where #(#filter::Fetch: FilterFetch,)*
{ {
type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>; type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>;
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
@ -283,15 +270,18 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
world: &'w World, world: &'w World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
let (#(#query,)*) = &state.0; QuerySet {
QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*)) 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,)* where #(#filter::Fetch: FilterFetch,)*
{ {
#(#query_fn)*
#(#query_fn_mut)* #(#query_fn_mut)*
} }
})); }));
@ -385,7 +375,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
TokenStream::from(quote! { TokenStream::from(quote! {
impl #impl_generics #path::system::SystemParam for #struct_name#ty_generics #where_clause { 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)] #[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; type Item = #struct_name#ty_generics;
unsafe fn get_param( unsafe fn get_param(
state: &'s mut Self, state: &'s mut Self,
@ -425,7 +415,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
#struct_name { #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(),)* #(#ignored_fields: <#ignored_field_types>::default(),)*
} }
} }
@ -436,43 +426,46 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
#[proc_macro_derive(SystemLabel)] #[proc_macro_derive(SystemLabel)]
pub fn derive_system_label(input: TokenStream) -> TokenStream { pub fn derive_system_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into()); derive_label(input, Ident::new("SystemLabel", Span::call_site())).into()
trait_path
.segments
.push(format_ident!("SystemLabel").into());
derive_label(input, trait_path)
} }
#[proc_macro_derive(StageLabel)] #[proc_macro_derive(StageLabel)]
pub fn derive_stage_label(input: TokenStream) -> TokenStream { pub fn derive_stage_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path(); derive_label(input, Ident::new("StageLabel", Span::call_site())).into()
trait_path.segments.push(format_ident!("schedule").into());
trait_path.segments.push(format_ident!("StageLabel").into());
derive_label(input, trait_path)
} }
#[proc_macro_derive(AmbiguitySetLabel)] #[proc_macro_derive(AmbiguitySetLabel)]
pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream { pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path(); derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into()
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("AmbiguitySetLabel").into());
derive_label(input, trait_path)
} }
#[proc_macro_derive(RunCriteriaLabel)] #[proc_macro_derive(RunCriteriaLabel)]
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream { pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput); let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path(); derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into()
trait_path.segments.push(format_ident!("schedule").into()); }
trait_path
.segments fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
.push(format_ident!("RunCriteriaLabel").into()); let ident = input.ident;
derive_label(input, trait_path) 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<dyn #ecs_path::schedule::#label_type> {
Box::new(Clone::clone(self))
}
}
}
} }
fn bevy_ecs_path() -> syn::Path { fn bevy_ecs_path() -> syn::Path {

View file

@ -15,26 +15,15 @@ use std::{
pub struct ArchetypeId(usize); pub struct ArchetypeId(usize);
impl ArchetypeId { impl ArchetypeId {
pub const EMPTY: ArchetypeId = ArchetypeId(0);
pub const RESOURCE: ArchetypeId = ArchetypeId(1);
pub const INVALID: ArchetypeId = ArchetypeId(usize::MAX);
#[inline] #[inline]
pub const fn new(index: usize) -> Self { pub const fn new(index: usize) -> Self {
ArchetypeId(index) 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] #[inline]
pub fn index(self) -> usize { pub fn index(self) -> usize {
self.0 self.0
@ -65,7 +54,7 @@ impl Edges {
} }
#[inline] #[inline]
pub fn set_add_bundle( pub fn insert_add_bundle(
&mut self, &mut self,
bundle_id: BundleId, bundle_id: BundleId,
archetype_id: ArchetypeId, archetype_id: ArchetypeId,
@ -86,7 +75,7 @@ impl Edges {
} }
#[inline] #[inline]
pub fn set_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option<ArchetypeId>) { pub fn insert_remove_bundle(&mut self, bundle_id: BundleId, archetype_id: Option<ArchetypeId>) {
self.remove_bundle.insert(bundle_id, archetype_id); self.remove_bundle.insert(bundle_id, archetype_id);
} }
@ -99,7 +88,7 @@ impl Edges {
} }
#[inline] #[inline]
pub fn set_remove_bundle_intersection( pub fn insert_remove_bundle_intersection(
&mut self, &mut self,
bundle_id: BundleId, bundle_id: BundleId,
archetype_id: Option<ArchetypeId>, archetype_id: Option<ArchetypeId>,
@ -115,8 +104,8 @@ struct TableInfo {
} }
pub(crate) struct ArchetypeSwapRemoveResult { pub(crate) struct ArchetypeSwapRemoveResult {
pub swapped_entity: Option<Entity>, pub(crate) swapped_entity: Option<Entity>,
pub table_row: usize, pub(crate) table_row: usize,
} }
pub(crate) struct ArchetypeComponentInfo { 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", // adds the resource archetype. it is "special" in that it is inaccessible via a "hash",
// which prevents entities from being added to it // which prevents entities from being added to it
archetypes.archetypes.push(Archetype::new( archetypes.archetypes.push(Archetype::new(
ArchetypeId::resource(), ArchetypeId::RESOURCE,
TableId::empty(), TableId::empty(),
Cow::Owned(Vec::new()), Cow::Owned(Vec::new()),
Cow::Owned(Vec::new()), Cow::Owned(Vec::new()),
@ -412,7 +401,7 @@ impl Archetypes {
#[inline] #[inline]
pub fn empty(&self) -> &Archetype { pub fn empty(&self) -> &Archetype {
// SAFE: empty archetype always exists // SAFE: empty archetype always exists
unsafe { self.archetypes.get_unchecked(ArchetypeId::empty().index()) } unsafe { self.archetypes.get_unchecked(ArchetypeId::EMPTY.index()) }
} }
#[inline] #[inline]
@ -420,17 +409,14 @@ impl Archetypes {
// SAFE: empty archetype always exists // SAFE: empty archetype always exists
unsafe { unsafe {
self.archetypes self.archetypes
.get_unchecked_mut(ArchetypeId::empty().index()) .get_unchecked_mut(ArchetypeId::EMPTY.index())
} }
} }
#[inline] #[inline]
pub fn resource(&self) -> &Archetype { pub fn resource(&self) -> &Archetype {
// SAFE: resource archetype always exists // SAFE: resource archetype always exists
unsafe { unsafe { self.archetypes.get_unchecked(ArchetypeId::RESOURCE.index()) }
self.archetypes
.get_unchecked(ArchetypeId::resource().index())
}
} }
#[inline] #[inline]
@ -438,7 +424,7 @@ impl Archetypes {
// SAFE: resource archetype always exists // SAFE: resource archetype always exists
unsafe { unsafe {
self.archetypes self.archetypes
.get_unchecked_mut(ArchetypeId::resource().index()) .get_unchecked_mut(ArchetypeId::RESOURCE.index())
} }
} }

View file

@ -1,10 +1,10 @@
pub use bevy_ecs_macros::Bundle; pub use bevy_ecs_macros::Bundle;
use crate::{ use crate::{
archetype::ComponentStatus, archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo}, component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::Entity, entity::{Entities, Entity, EntityLocation},
storage::{SparseSetIndex, SparseSets, Table}, storage::{SparseSetIndex, SparseSets, Storages, Table},
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use std::{any::TypeId, collections::HashMap}; 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 /// An ordered collection of components, commonly used for spawning entities, and adding and
/// removing components in bulk. /// 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`. /// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`.
/// The `Bundle` trait is automatically implemented for tuples of components: /// The `Bundle` trait is automatically implemented for tuples of components:
/// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections /// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections
@ -38,13 +36,13 @@ use std::{any::TypeId, collections::HashMap};
/// ``` /// ```
/// ///
/// # Safety /// # 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. /// _exact_ order that [Bundle::get_components] is called.
/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by /// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by
/// [Bundle::type_info] /// [Bundle::component_id]
pub unsafe trait Bundle: Send + Sync + 'static { pub unsafe trait Bundle: Send + Sync + 'static {
/// Gets this [Bundle]'s components type info, in the order of this bundle's Components /// Gets this [Bundle]'s component ids, in the order of this bundle's Components
fn type_info() -> Vec<TypeInfo>; fn component_ids(components: &mut Components) -> Vec<ComponentId>;
/// Calls `func`, which should return data for each component in the bundle, in the order of /// Calls `func`, which should return data for each component in the bundle, in the order of
/// this bundle's Components /// this bundle's Components
@ -66,8 +64,9 @@ macro_rules! tuple_impl {
($($name: ident),*) => { ($($name: ident),*) => {
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order /// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) { unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
fn type_info() -> Vec<TypeInfo> { #[allow(unused_variables)]
vec![$(TypeInfo::of::<$name>()),*] fn component_ids(components: &mut Components) -> Vec<ComponentId> {
vec![$(components.get_or_insert_id::<$name>()),*]
} }
#[allow(unused_variables, unused_mut)] #[allow(unused_variables, unused_mut)]
@ -123,19 +122,119 @@ pub struct BundleInfo {
} }
impl BundleInfo { impl BundleInfo {
/// # Safety
/// table row must exist, entity must be valid
#[allow(clippy::too_many_arguments)]
#[inline] #[inline]
pub(crate) unsafe fn write_components<T: Bundle>( pub fn id(&self) -> BundleId {
&self, self.id
sparse_sets: &mut SparseSets, }
entity: Entity,
table: &mut Table, #[inline]
table_row: usize, pub fn components(&self) -> &[ComponentId] {
bundle_status: &[ComponentStatus], &self.component_ids
bundle: T, }
#[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, 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` must be the "new" table for `entity`. `table_row` must have space allocated for the `entity`, `bundle` must match this BundleInfo's type
#[inline]
#[allow(clippy::too_many_arguments)]
unsafe fn write_components<T: Bundle>(
&self,
table: &mut Table,
sparse_sets: &mut SparseSets,
add_bundle: &AddBundle,
entity: Entity,
table_row: usize,
change_tick: u32,
bundle: T,
) { ) {
// NOTE: get_components calls this closure on each component in "bundle order". // NOTE: get_components calls this closure on each component in "bundle order".
// bundle_info.component_ids are also in "bundle order" // bundle_info.component_ids are also in "bundle order"
@ -145,7 +244,7 @@ impl BundleInfo {
match self.storage_types[bundle_component] { match self.storage_types[bundle_component] {
StorageType::Table => { StorageType::Table => {
let column = table.get_column_mut(component_id).unwrap(); 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 => { ComponentStatus::Added => {
column.initialize( column.initialize(
table_row, 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] #[inline]
pub fn id(&self) -> BundleId { pub unsafe fn insert<T: Bundle>(
self.id &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 unsafe fn spawn_non_existent<T: Bundle>(
&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] #[inline]
pub fn components(&self) -> &[ComponentId] { pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
&self.component_ids let entity = self.entities.alloc();
} // SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type
self.spawn_non_existent(entity, bundle);
#[inline] entity
pub fn storage_types(&self) -> &[StorageType] {
&self.storage_types
} }
} }
@ -206,10 +563,12 @@ impl Bundles {
) -> &'a BundleInfo { ) -> &'a BundleInfo {
let bundle_infos = &mut self.bundle_infos; let bundle_infos = &mut self.bundle_infos;
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| { let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
let type_info = T::type_info(); let component_ids = T::component_ids(components);
let id = BundleId(bundle_infos.len()); let id = BundleId(bundle_infos.len());
let bundle_info = // SAFE: T::component_id ensures info was created
initialize_bundle(std::any::type_name::<T>(), &type_info, id, components); let bundle_info = unsafe {
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
};
bundle_infos.push(bundle_info); bundle_infos.push(bundle_info);
id 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, bundle_type_name: &'static str,
type_info: &[TypeInfo], component_ids: Vec<ComponentId>,
id: BundleId, id: BundleId,
components: &mut Components, components: &mut Components,
) -> BundleInfo { ) -> BundleInfo {
let mut component_ids = Vec::new();
let mut storage_types = Vec::new(); let mut storage_types = Vec::new();
for type_info in type_info { for &component_id in &component_ids {
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone()); // SAFE: component_id exists and is therefore valid
// SAFE: get_with_type_info ensures info was created let component_info = components.get_info_unchecked(component_id);
let info = unsafe { components.get_info_unchecked(component_id) }; storage_types.push(component_info.storage_type());
component_ids.push(component_id);
storage_types.push(info.storage_type());
} }
let mut deduped = component_ids.clone(); let mut deduped = component_ids.clone();

View file

@ -1,7 +1,3 @@
mod type_info;
pub use type_info::*;
use crate::storage::SparseSetIndex; use crate::storage::SparseSetIndex;
use std::{ use std::{
alloc::Layout, alloc::Layout,
@ -56,15 +52,8 @@ impl Default for StorageType {
#[derive(Debug)] #[derive(Debug)]
pub struct ComponentInfo { pub struct ComponentInfo {
name: String,
id: ComponentId, id: ComponentId,
type_id: Option<TypeId>, descriptor: ComponentDescriptor,
// 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,
} }
impl ComponentInfo { impl ComponentInfo {
@ -75,44 +64,36 @@ impl ComponentInfo {
#[inline] #[inline]
pub fn name(&self) -> &str { pub fn name(&self) -> &str {
&self.name &self.descriptor.name
} }
#[inline] #[inline]
pub fn type_id(&self) -> Option<TypeId> { pub fn type_id(&self) -> Option<TypeId> {
self.type_id self.descriptor.type_id
} }
#[inline] #[inline]
pub fn layout(&self) -> Layout { pub fn layout(&self) -> Layout {
self.layout self.descriptor.layout
} }
#[inline] #[inline]
pub fn drop(&self) -> unsafe fn(*mut u8) { pub fn drop(&self) -> unsafe fn(*mut u8) {
self.drop self.descriptor.drop
} }
#[inline] #[inline]
pub fn storage_type(&self) -> StorageType { pub fn storage_type(&self) -> StorageType {
self.storage_type self.descriptor.storage_type
} }
#[inline] #[inline]
pub fn is_send_and_sync(&self) -> bool { 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 { fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
ComponentInfo { ComponentInfo { id, descriptor }
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,
}
} }
} }
@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId {
} }
} }
#[derive(Debug)]
pub struct ComponentDescriptor { pub struct ComponentDescriptor {
name: String, name: String,
storage_type: StorageType, storage_type: StorageType,
@ -154,6 +136,11 @@ pub struct ComponentDescriptor {
} }
impl 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<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
pub fn new<T: Component>(storage_type: StorageType) -> Self { pub fn new<T: Component>(storage_type: StorageType) -> Self {
Self { Self {
name: std::any::type_name::<T>().to_string(), name: std::any::type_name::<T>().to_string(),
@ -161,7 +148,18 @@ impl ComponentDescriptor {
is_send_and_sync: true, is_send_and_sync: true,
type_id: Some(TypeId::of::<T>()), type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(), layout: Layout::new::<T>(),
drop: TypeInfo::drop_ptr::<T>, drop: Self::drop_ptr::<T>,
}
}
fn new_non_send<T: Any>(storage_type: StorageType) -> Self {
Self {
name: std::any::type_name::<T>().to_string(),
storage_type,
is_send_and_sync: false,
type_id: Some(TypeId::of::<T>()),
layout: Layout::new::<T>(),
drop: Self::drop_ptr::<T>,
} }
} }
@ -181,19 +179,6 @@ impl ComponentDescriptor {
} }
} }
impl From<TypeInfo> 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)] #[derive(Debug, Default)]
pub struct Components { pub struct Components {
components: Vec<ComponentInfo>, components: Vec<ComponentInfo>,
@ -231,7 +216,12 @@ impl Components {
#[inline] #[inline]
pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId { pub fn get_or_insert_id<T: Component>(&mut self) -> ComponentId {
self.get_or_insert_with(TypeId::of::<T>(), TypeInfo::of::<T>) // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.get_or_insert_with(TypeId::of::<T>(), || {
ComponentDescriptor::new::<T>(StorageType::default())
})
}
} }
#[inline] #[inline]
@ -279,42 +269,58 @@ impl Components {
#[inline] #[inline]
pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId { pub fn get_or_insert_resource_id<T: Component>(&mut self) -> ComponentId {
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of::<T>) // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new::<T>(StorageType::default())
})
}
} }
#[inline] #[inline]
pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId { pub fn get_or_insert_non_send_resource_id<T: Any>(&mut self) -> ComponentId {
self.get_or_insert_resource_with(TypeId::of::<T>(), TypeInfo::of_non_send_and_sync::<T>) // SAFE: The [`ComponentDescriptor`] matches the [`TypeId`]
unsafe {
self.get_or_insert_resource_with(TypeId::of::<T>(), || {
ComponentDescriptor::new_non_send::<T>(StorageType::default())
})
}
} }
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`]
#[inline] #[inline]
fn get_or_insert_resource_with( unsafe fn get_or_insert_resource_with(
&mut self, &mut self,
type_id: TypeId, type_id: TypeId,
func: impl FnOnce() -> TypeInfo, func: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId { ) -> ComponentId {
let components = &mut self.components; let components = &mut self.components;
let index = self.resource_indices.entry(type_id).or_insert_with(|| { let index = self.resource_indices.entry(type_id).or_insert_with(|| {
let type_info = func(); let descriptor = func();
let index = components.len(); let index = components.len();
components.push(ComponentInfo::new(ComponentId(index), type_info.into())); components.push(ComponentInfo::new(ComponentId(index), descriptor));
index index
}); });
ComponentId(*index) ComponentId(*index)
} }
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`]
#[inline] #[inline]
pub(crate) fn get_or_insert_with( pub(crate) unsafe fn get_or_insert_with(
&mut self, &mut self,
type_id: TypeId, type_id: TypeId,
func: impl FnOnce() -> TypeInfo, func: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId { ) -> ComponentId {
let components = &mut self.components; let components = &mut self.components;
let index = self.indices.entry(type_id).or_insert_with(|| { let index = self.indices.entry(type_id).or_insert_with(|| {
let type_info = func(); let descriptor = func();
let index = components.len(); let index = components.len();
components.push(ComponentInfo::new(ComponentId(index), type_info.into())); components.push(ComponentInfo::new(ComponentId(index), descriptor));
index index
}); });

View file

@ -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<T: Send + Sync + 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
layout: Layout::new::<T>(),
is_send_and_sync: true,
drop: Self::drop_ptr::<T>,
type_name: core::any::type_name::<T>(),
}
}
pub fn of_non_send_and_sync<T: 'static>() -> Self {
Self {
type_id: TypeId::of::<T>(),
layout: Layout::new::<T>(),
is_send_and_sync: false,
drop: Self::drop_ptr::<T>,
type_name: core::any::type_name::<T>(),
}
}
#[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<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
}

View file

@ -34,6 +34,11 @@ pub enum AllocAtWithoutReplacement {
impl Entity { impl Entity {
/// Creates a new entity reference with a generation of 0. /// 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 { pub fn new(id: u32) -> Entity {
Entity { id, generation: 0 } Entity { id, generation: 0 }
} }
@ -168,6 +173,7 @@ pub struct Entities {
/// Once `flush()` is done, `free_cursor` will equal `pending.len()`. /// Once `flush()` is done, `free_cursor` will equal `pending.len()`.
pending: Vec<u32>, pending: Vec<u32>,
free_cursor: AtomicI64, free_cursor: AtomicI64,
/// Stores the number of free entities for [`len`](Entities::len)
len: u32, len: u32,
} }
@ -318,7 +324,7 @@ impl Entities {
AllocAtWithoutReplacement::DidNotExist AllocAtWithoutReplacement::DidNotExist
} else { } else {
let current_meta = &mut self.meta[entity.id as usize]; 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 AllocAtWithoutReplacement::DidNotExist
} else if current_meta.generation == entity.generation { } else if current_meta.generation == entity.generation {
AllocAtWithoutReplacement::Exists(current_meta.location) 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 { pub fn contains(&self, entity: Entity) -> bool {
// Note that out-of-range IDs are considered to be "contained" because self.resolve_from_id(entity.id())
// they must be reserved IDs that we haven't flushed yet. .map_or(false, |e| e.generation() == entity.generation)
self.meta
.get(entity.id as usize)
.map_or(true, |meta| meta.generation == entity.generation)
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
@ -384,7 +390,7 @@ impl Entities {
if (entity.id as usize) < self.meta.len() { if (entity.id as usize) < self.meta.len() {
let meta = &self.meta[entity.id as usize]; let meta = &self.meta[entity.id as usize];
if meta.generation != entity.generation if meta.generation != entity.generation
|| meta.location.archetype_id == ArchetypeId::invalid() || meta.location.archetype_id == ArchetypeId::INVALID
{ {
return None; return None;
} }
@ -394,31 +400,23 @@ impl Entities {
} }
} }
/// Panics if the given id would represent an index outside of `meta`. /// 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
/// ///
/// # Safety /// Note: This method may return [`Entities`](Entity) which are currently free
/// /// Note that [`contains`](Entities::contains) will correctly return false for freed
/// Must only be called for currently allocated `id`s. /// entities, since it checks the generation
pub unsafe fn resolve_unknown_gen(&self, id: u32) -> Entity { pub fn resolve_from_id(&self, id: u32) -> Option<Entity> {
let meta_len = self.meta.len(); let idu = id as usize;
if let Some(&EntityMeta { generation, .. }) = self.meta.get(idu) {
if meta_len > id as usize { Some(Entity { generation, id })
let meta = &self.meta[id as usize];
Entity {
generation: meta.generation,
id,
}
} else { } 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 free_cursor = self.free_cursor.load(Ordering::Relaxed);
let num_pending = std::cmp::max(-free_cursor, 0) as usize; // If this entity was manually created, then free_cursor might be positive
// Returning None handles that case correctly
if meta_len + num_pending > id as usize { let num_pending = usize::try_from(-free_cursor).ok()?;
// Pending entities will have generation 0. (idu < self.meta.len() + num_pending).then(|| Entity { generation: 0, id })
Entity { generation: 0, id }
} else {
panic!("entity id is out of range");
}
} }
} }
@ -431,7 +429,7 @@ impl Entities {
/// ///
/// # Safety /// # Safety
/// Flush _must_ set the entity location to the correct ArchetypeId for the given Entity /// 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. /// not been assigned to an Archetype.
pub unsafe fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) { pub unsafe fn flush(&mut self, mut init: impl FnMut(Entity, &mut EntityLocation)) {
let free_cursor = self.free_cursor.get_mut(); let free_cursor = self.free_cursor.get_mut();
@ -476,7 +474,7 @@ impl Entities {
pub fn flush_as_invalid(&mut self) { pub fn flush_as_invalid(&mut self) {
unsafe { unsafe {
self.flush(|_entity, location| { self.flush(|_entity, location| {
location.archetype_id = ArchetypeId::invalid(); location.archetype_id = ArchetypeId::INVALID;
}) })
} }
} }
@ -502,8 +500,8 @@ impl EntityMeta {
const EMPTY: EntityMeta = EntityMeta { const EMPTY: EntityMeta = EntityMeta {
generation: 0, generation: 0,
location: EntityLocation { location: EntityLocation {
archetype_id: ArchetypeId::invalid(), archetype_id: ArchetypeId::INVALID,
index: usize::max_value(), // dummy value, to be filled in index: usize::MAX, // dummy value, to be filled in
}, },
}; };
} }

View file

@ -68,7 +68,7 @@ enum State {
/// [`Events::update`] exactly once per update/frame. /// [`Events::update`] exactly once per update/frame.
/// ///
/// [`Events::update_system`] is a system that does this, typically intialized automatically using /// [`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. /// least once per loop/frame.
/// Events will persist across a single frame boundary and so ordering of event producers and /// 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). /// 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 /// An alternative call pattern would be to call [Events::update] manually across frames to control
/// when events are cleared. /// when events are cleared.
/// This complicates consumption and risks ever-expanding memory usage if not cleaned up, /// 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)] #[derive(Debug)]
pub struct Events<T> { pub struct Events<T> {
events_a: Vec<EventInstance<T>>, events_a: Vec<EventInstance<T>>,
@ -151,21 +151,20 @@ fn map_instance_event<T>(event_instance: &EventInstance<T>) -> &T {
/// Reads events of type `T` in order and tracks which events have already been read. /// Reads events of type `T` in order and tracks which events have already been read.
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct EventReader<'s, 'w, T: Component> { pub struct EventReader<'w, 's, T: Component> {
last_event_count: Local<'s, (usize, PhantomData<T>)>, last_event_count: Local<'s, (usize, PhantomData<T>)>,
events: Res<'w, Events<T>>, events: Res<'w, Events<T>>,
} }
/// Sends events of type `T`. /// Sends events of type `T`.
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct EventWriter<'s, 'w, T: Component> { pub struct EventWriter<'w, 's, T: Component> {
events: ResMut<'w, Events<T>>, events: ResMut<'w, Events<T>>,
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
#[system_param(ignore)] #[system_param(ignore)]
marker: PhantomData<&'s usize>, 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) { pub fn send(&mut self, event: T) {
self.events.send(event); 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 /// 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 /// event counter, which means subsequent event reads will not include events that happened
/// before now. /// before now.

View file

@ -22,7 +22,7 @@ pub mod prelude {
change_detection::DetectChanges, change_detection::DetectChanges,
entity::Entity, entity::Entity,
event::{EventReader, EventWriter}, event::{EventReader, EventWriter},
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without}, query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without},
schedule::{ schedule::{
AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion, AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping, RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping,
@ -41,7 +41,7 @@ mod tests {
use crate as bevy_ecs; use crate as bevy_ecs;
use crate::{ use crate::{
bundle::Bundle, bundle::Bundle,
component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo}, component::{Component, ComponentDescriptor, ComponentId, StorageType},
entity::Entity, entity::Entity,
query::{ query::{
Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery, Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery,
@ -60,7 +60,9 @@ mod tests {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct A(usize); struct A(usize);
#[derive(Debug, PartialEq, Eq)]
struct B(usize); struct B(usize);
#[derive(Debug, PartialEq, Eq)]
struct C; struct C;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -102,21 +104,26 @@ mod tests {
#[test] #[test]
fn bundle_derive() { fn bundle_derive() {
let mut world = World::new();
#[derive(Bundle, PartialEq, Debug)] #[derive(Bundle, PartialEq, Debug)]
struct Foo { struct Foo {
x: &'static str, x: &'static str,
y: i32, y: i32,
} }
assert_eq!(
<Foo as Bundle>::type_info(),
vec![TypeInfo::of::<&'static str>(), TypeInfo::of::<i32>(),]
);
let mut world = World::new();
world world
.register_component(ComponentDescriptor::new::<i32>(StorageType::SparseSet)) .register_component(ComponentDescriptor::new::<i32>(StorageType::SparseSet))
.unwrap(); .unwrap();
assert_eq!(
<Foo as Bundle>::component_ids(world.components_mut()),
vec![
world.components_mut().get_or_insert_id::<&'static str>(),
world.components_mut().get_or_insert_id::<i32>(),
]
);
let e1 = world.spawn().insert_bundle(Foo { x: "abc", y: 123 }).id(); let e1 = world.spawn().insert_bundle(Foo { x: "abc", y: 123 }).id();
let e2 = world.spawn().insert_bundle(("def", 456, true)).id(); let e2 = world.spawn().insert_bundle(("def", 456, true)).id();
assert_eq!(*world.get::<&str>(e1).unwrap(), "abc"); assert_eq!(*world.get::<&str>(e1).unwrap(), "abc");
@ -146,12 +153,12 @@ mod tests {
} }
assert_eq!( assert_eq!(
<Nested as Bundle>::type_info(), <Nested as Bundle>::component_ids(world.components_mut()),
vec![ vec![
TypeInfo::of::<usize>(), world.components_mut().get_or_insert_id::<usize>(),
TypeInfo::of::<&'static str>(), world.components_mut().get_or_insert_id::<&'static str>(),
TypeInfo::of::<i32>(), world.components_mut().get_or_insert_id::<i32>(),
TypeInfo::of::<u8>(), world.components_mut().get_or_insert_id::<u8>(),
] ]
); );
@ -1393,4 +1400,89 @@ mod tests {
"space between original entities and high entities is used for new entity ids" "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::<A>(e0),
Some(&A(0)),
"existing component was preserved"
);
assert_eq!(
world.get::<B>(e0),
Some(&B(0)),
"pre-existing entity received correct B component"
);
assert_eq!(
world.get::<B>(e1),
Some(&B(1)),
"new entity was spawned and received correct B component"
);
assert_eq!(
world.get::<C>(e0),
Some(&C),
"pre-existing entity received C component"
);
assert_eq!(
world.get::<C>(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::<A>(e0),
Some(&A(0)),
"existing component was preserved"
);
assert_eq!(
world.get::<B>(e0),
Some(&B(0)),
"pre-existing entity received correct B component"
);
assert_eq!(
world.get::<B>(e1),
Some(&B(1)),
"new entity was spawned and received correct B component"
);
assert_eq!(
world.get::<C>(e0),
Some(&C),
"pre-existing entity received C component"
);
assert_eq!(
world.get::<C>(e1),
Some(&C),
"new entity was spawned and received C component"
);
}
} }

View file

@ -137,6 +137,7 @@ impl WorldQuery for Entity {
} }
/// The [`Fetch`] of [`Entity`]. /// The [`Fetch`] of [`Entity`].
#[derive(Clone)]
pub struct EntityFetch { pub struct EntityFetch {
entities: *const Entity, entities: *const Entity,
} }
@ -573,6 +574,7 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
} }
/// The [`Fetch`] of `Option<T>`. /// The [`Fetch`] of `Option<T>`.
#[derive(Clone)]
pub struct OptionFetch<T> { pub struct OptionFetch<T> {
fetch: T, fetch: T,
matches: bool, matches: bool,
@ -807,6 +809,21 @@ pub struct ChangeTrackersFetch<T> {
change_tick: u32, change_tick: u32,
} }
impl<T> Clone for ChangeTrackersFetch<T> {
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 /// SAFETY: access is read only
unsafe impl<T> ReadOnlyFetch for ChangeTrackersFetch<T> {} unsafe impl<T> ReadOnlyFetch for ChangeTrackersFetch<T> {}

View file

@ -1,6 +1,5 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeComponentId}, archetype::{Archetype, ArchetypeComponentId},
bundle::Bundle,
component::{Component, ComponentId, ComponentTicks, StorageType}, component::{Component, ComponentId, ComponentTicks, StorageType},
entity::Entity, entity::Entity,
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery}, 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. /// Extension trait for [`Fetch`] containing methods used by query filters.
/// This trait exists to allow "short circuit" behaviors for relevant query filter fetches. /// 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 /// # Safety
/// ///
/// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range /// 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<T> {
} }
} }
pub struct WithBundle<T: Bundle>(PhantomData<T>);
impl<T: Bundle> WorldQuery for WithBundle<T> {
type Fetch = WithBundleFetch<T>;
type State = WithBundleState<T>;
}
pub struct WithBundleFetch<T: Bundle> {
is_dense: bool,
marker: PhantomData<T>,
}
pub struct WithBundleState<T: Bundle> {
component_ids: Vec<ComponentId>,
is_dense: bool,
marker: PhantomData<T>,
}
// SAFETY: no component access or archetype component access
unsafe impl<T: Bundle> FetchState for WithBundleState<T> {
fn init(world: &mut World) -> Self {
let bundle_info = world.bundles.init_info::<T>(&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<ComponentId>) {
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<ArchetypeComponentId>,
) {
}
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<T> {
type Item = bool;
type State = WithBundleState<T>;
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. /// 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 /// This is useful for example if a system with multiple components in a query only wants to run

View file

@ -4,7 +4,7 @@ use crate::{
storage::{TableId, Tables}, storage::{TableId, Tables},
world::World, world::World,
}; };
use std::{marker::PhantomData, mem::MaybeUninit}; use std::mem::MaybeUninit;
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query). /// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
/// ///
@ -217,7 +217,7 @@ where
archetypes: &'w Archetypes, archetypes: &'w Archetypes,
query_state: &'s QueryState<Q, F>, query_state: &'s QueryState<Q, F>,
world: &'w World, 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> 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 // There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit
// TODO: use MaybeUninit::uninit_array if it stabilizes // TODO: use MaybeUninit::uninit_array if it stabilizes
let mut cursors: [MaybeUninit<QueryIterationCursor<'w, 's, Q, F>>; K] = let mut cursors: [MaybeUninit<QueryIterationCursor<'s, Q, F>>; K] =
MaybeUninit::uninit().assume_init(); MaybeUninit::uninit().assume_init();
for (i, cursor) in cursors.iter_mut().enumerate() { for (i, cursor) in cursors.iter_mut().enumerate() {
match i { match i {
@ -259,8 +259,8 @@ where
} }
// TODO: use MaybeUninit::array_assume_init if it stabilizes // TODO: use MaybeUninit::array_assume_init if it stabilizes
let cursors: [QueryIterationCursor<'w, 's, Q, F>; K] = let cursors: [QueryIterationCursor<'s, Q, F>; K] =
(&cursors as *const _ as *const [QueryIterationCursor<'w, 's, Q, F>; K]).read(); (&cursors as *const _ as *const [QueryIterationCursor<'s, Q, F>; K]).read();
QueryCombinationIter { QueryCombinationIter {
world, world,
@ -277,9 +277,9 @@ where
/// references to the same component, leading to unique reference aliasing. /// references to the same component, leading to unique reference aliasing.
///. ///.
/// It is always safe for shared access. /// It is always safe for shared access.
unsafe fn fetch_next_aliased_unchecked( unsafe fn fetch_next_aliased_unchecked<'a>(
&mut self, &mut self,
) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]> ) -> Option<[<Q::Fetch as Fetch<'a, 's>>::Item; K]>
where where
Q::Fetch: Clone, Q::Fetch: Clone,
F::Fetch: Clone, F::Fetch: Clone,
@ -309,7 +309,7 @@ where
} }
// TODO: use MaybeUninit::uninit_array if it stabilizes // TODO: use MaybeUninit::uninit_array if it stabilizes
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'w, 's>>::Item>; K] = let mut values: [MaybeUninit<<Q::Fetch as Fetch<'a, 's>>::Item>; K] =
MaybeUninit::uninit().assume_init(); MaybeUninit::uninit().assume_init();
for (value, cursor) in values.iter_mut().zip(&mut self.cursors) { 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 // TODO: use MaybeUninit::array_assume_init if it stabilizes
let values: [<Q::Fetch as Fetch<'w, 's>>::Item; K] = let values: [<Q::Fetch as Fetch<'a, 's>>::Item; K] =
(&values as *const _ as *const [<Q::Fetch as Fetch<'w, 's>>::Item; K]).read(); (&values as *const _ as *const [<Q::Fetch as Fetch<'a, 's>>::Item; K]).read();
Some(values) Some(values)
} }
/// Get next combination of queried components /// Get next combination of queried components
#[inline] #[inline]
pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]> pub fn fetch_next(&mut self) -> Option<[<Q::Fetch as Fetch<'_, 's>>::Item; K]>
where where
Q::Fetch: Clone, Q::Fetch: Clone,
F::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>, table_id_iter: std::slice::Iter<'s, TableId>,
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>, archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
fetch: Q::Fetch, fetch: Q::Fetch,
@ -408,10 +408,9 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
current_len: usize, current_len: usize,
current_index: usize, current_index: usize,
is_dense: bool, 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 where
Q::Fetch: Clone, Q::Fetch: Clone,
F::Fetch: Clone, F::Fetch: Clone,
@ -425,12 +424,11 @@ where
current_len: self.current_len, current_len: self.current_len,
current_index: self.current_index, current_index: self.current_index,
is_dense: self.is_dense, 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 where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
{ {
@ -473,13 +471,12 @@ where
archetype_id_iter: query_state.matched_archetype_ids.iter(), archetype_id_iter: query_state.matched_archetype_ids.iter(),
current_len: 0, current_len: 0,
current_index: 0, current_index: 0,
marker: PhantomData,
} }
} }
/// retrieve item returned from most recent `next` call again. /// retrieve item returned from most recent `next` call again.
#[inline] #[inline]
unsafe fn peek_last(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> { unsafe fn peek_last<'w>(&mut self) -> Option<<Q::Fetch as Fetch<'w, 's>>::Item> {
if self.current_index > 0 { if self.current_index > 0 {
if self.is_dense { if self.is_dense {
Some(self.fetch.table_fetch(self.current_index - 1)) 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 // 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. // We can't currently reuse QueryIterationCursor in QueryIter for performance reasons. See #1763 for context.
#[inline(always)] #[inline(always)]
unsafe fn next( unsafe fn next<'w>(
&mut self, &mut self,
tables: &'w Tables, tables: &'w Tables,
archetypes: &'w Archetypes, archetypes: &'w Archetypes,

View file

@ -13,6 +13,7 @@ use bevy_tasks::TaskPool;
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
use thiserror::Error; use thiserror::Error;
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
pub struct QueryState<Q: WorldQuery, F: WorldQuery = ()> pub struct QueryState<Q: WorldQuery, F: WorldQuery = ()>
where where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
@ -35,6 +36,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F>
where where
F::Fetch: FilterFetch, 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 { pub fn new(world: &mut World) -> Self {
let fetch_state = <Q::State as FetchState>::init(world); let fetch_state = <Q::State as FetchState>::init(world);
let filter_state = <F::State as FetchState>::init(world); let filter_state = <F::State as FetchState>::init(world);
@ -68,6 +70,7 @@ where
state state
} }
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
#[inline] #[inline]
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool { 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 // SAFE: the iterator is instantly consumed via `none_remaining` and the implementation of
@ -78,6 +81,12 @@ where
} }
} }
/// 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 update_archetypes(&mut self, world: &World) { pub fn update_archetypes(&mut self, world: &World) {
self.validate_world(world); self.validate_world(world);
let archetypes = world.archetypes(); let archetypes = world.archetypes();
@ -98,6 +107,7 @@ where
} }
} }
/// Creates a new [`Archetype`].
pub fn new_archetype(&mut self, archetype: &Archetype) { pub fn new_archetype(&mut self, archetype: &Archetype) {
if self.fetch_state.matches_archetype(archetype) if self.fetch_state.matches_archetype(archetype)
&& self.filter_state.matches_archetype(archetype) && self.filter_state.matches_archetype(archetype)
@ -121,6 +131,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] #[inline]
pub fn get<'w, 's>( pub fn get<'w, 's>(
&'s mut self, &'s mut self,
@ -134,6 +147,17 @@ where
unsafe { self.get_unchecked(world, entity) } unsafe { self.get_unchecked(world, entity) }
} }
/// Gets the query result for the given [`World`] and [`Entity`].
#[inline]
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
// SAFETY: query has unique world access
unsafe { self.get_unchecked(world, entity) }
}
#[inline] #[inline]
pub fn get_manual<'w, 's>( pub fn get_manual<'w, 's>(
&'s self, &'s self,
@ -155,16 +179,8 @@ where
} }
} }
#[inline] /// Gets the query result for the given [`World`] and [`Entity`].
pub fn get_mut<'w, 's>( ///
&'s mut self,
world: &'w mut World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
// SAFETY: query has unique world access
unsafe { self.get_unchecked(world, entity) }
}
/// # Safety /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -184,7 +200,11 @@ where
) )
} }
/// Gets the query result for the given [`World`] and [`Entity`], where the last change and
/// the current change tick are given.
///
/// # Safety /// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
pub unsafe fn get_unchecked_manual<'w, 's>( pub unsafe fn get_unchecked_manual<'w, 's>(
@ -219,6 +239,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] #[inline]
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F> pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F>
where where
@ -228,6 +251,13 @@ where
unsafe { self.iter_unchecked(world) } unsafe { self.iter_unchecked(world) }
} }
/// 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) }
}
#[inline] #[inline]
pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F> pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F>
where where
@ -240,12 +270,16 @@ where
} }
} }
#[inline] /// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> { /// This can only be called for read-only queries.
// SAFETY: query has unique world access ///
unsafe { self.iter_unchecked(world) } /// 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] #[inline]
pub fn iter_combinations<'w, 's, const K: usize>( pub fn iter_combinations<'w, 's, const K: usize>(
&'s mut self, &'s mut self,
@ -258,6 +292,13 @@ where
unsafe { self.iter_combinations_unchecked(world) } 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] #[inline]
pub fn iter_combinations_mut<'w, 's, const K: usize>( pub fn iter_combinations_mut<'w, 's, const K: usize>(
&'s mut self, &'s mut self,
@ -267,6 +308,8 @@ where
unsafe { self.iter_combinations_unchecked(world) } unsafe { self.iter_combinations_unchecked(world) }
} }
/// Returns an [`Iterator`] over the query results for the given [`World`].
///
/// # Safety /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -280,6 +323,10 @@ where
self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick()) 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 /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -297,7 +344,11 @@ where
) )
} }
/// Returns an [`Iterator`] for the given [`World`], where the last change and
/// the current change tick are given.
///
/// # Safety /// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
@ -312,7 +363,12 @@ where
QueryIter::new(world, self, last_change_tick, change_tick) 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 /// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query. /// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world` /// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
@ -327,6 +383,10 @@ where
QueryCombinationIter::new(world, self, last_change_tick, change_tick) 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] #[inline]
pub fn for_each<'w, 's>( pub fn for_each<'w, 's>(
&'s mut self, &'s mut self,
@ -341,6 +401,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] #[inline]
pub fn for_each_mut<'w, 's>( pub fn for_each_mut<'w, 's>(
&'s mut self, &'s mut self,
@ -353,6 +415,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 /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -372,6 +439,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] #[inline]
pub fn par_for_each<'w, 's>( pub fn par_for_each<'w, 's>(
&'s mut self, &'s mut self,
@ -388,6 +459,7 @@ where
} }
} }
/// Runs `func` on each query result in parallel using the given `task_pool`.
#[inline] #[inline]
pub fn par_for_each_mut<'w, 's>( pub fn par_for_each_mut<'w, 's>(
&'s mut self, &'s mut self,
@ -402,6 +474,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 /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -425,6 +501,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 /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -477,6 +557,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 /// # Safety
/// ///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries /// This does not check for mutable query correctness. To be safe, make sure mutable queries

View file

@ -10,6 +10,7 @@ use bevy_reflect::{impl_reflect_value, FromType, Reflect, ReflectDeserialize};
pub struct ReflectComponent { pub struct ReflectComponent {
add_component: fn(&mut World, Entity, &dyn Reflect), add_component: fn(&mut World, Entity, &dyn Reflect),
apply_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: fn(&World, Entity) -> Option<&dyn Reflect>,
reflect_component_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>, reflect_component_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
copy_component: fn(&World, &mut World, Entity, Entity), copy_component: fn(&World, &mut World, Entity, Entity),
@ -24,6 +25,10 @@ impl ReflectComponent {
(self.apply_component)(world, entity, component); (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>( pub fn reflect_component<'a>(
&self, &self,
world: &'a World, world: &'a World,
@ -83,6 +88,9 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
let mut component = world.get_mut::<C>(entity).unwrap(); let mut component = world.get_mut::<C>(entity).unwrap();
component.apply(reflected_component); component.apply(reflected_component);
}, },
remove_component: |world, entity| {
world.entity_mut(entity).remove::<C>();
},
copy_component: |source_world, destination_world, source_entity, destination_entity| { copy_component: |source_world, destination_world, source_entity, destination_entity| {
let source_component = source_world.get::<C>(source_entity).unwrap(); let source_component = source_world.get::<C>(source_entity).unwrap();
let mut destination_component = C::from_world(destination_world); let mut destination_component = C::from_world(destination_world);

View file

@ -316,7 +316,7 @@ mod tests {
use super::SchedulingEvent::{self, *}; use super::SchedulingEvent::{self, *};
use crate::{ use crate::{
schedule::{SingleThreadedExecutor, Stage, SystemStage}, schedule::{SingleThreadedExecutor, Stage, SystemStage},
system::{IntoSystem, NonSend, Query, Res, ResMut}, system::{NonSend, Query, Res, ResMut},
world::World, world::World,
}; };
use async_channel::Receiver; use async_channel::Receiver;
@ -338,9 +338,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
fn wants_for_nothing() {} fn wants_for_nothing() {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_for_nothing.system()) .with_system(wants_for_nothing)
.with_system(wants_for_nothing.system()) .with_system(wants_for_nothing)
.with_system(wants_for_nothing.system()); .with_system(wants_for_nothing);
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
@ -356,24 +356,24 @@ mod tests {
fn wants_mut(_: ResMut<usize>) {} fn wants_mut(_: ResMut<usize>) {}
fn wants_ref(_: Res<usize>) {} fn wants_ref(_: Res<usize>) {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut.system()) .with_system(wants_mut)
.with_system(wants_mut.system()); .with_system(wants_mut);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
receive_events(&world), receive_events(&world),
vec![StartedSystems(1), StartedSystems(1),] vec![StartedSystems(1), StartedSystems(1),]
); );
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut.system()) .with_system(wants_mut)
.with_system(wants_ref.system()); .with_system(wants_ref);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
receive_events(&world), receive_events(&world),
vec![StartedSystems(1), StartedSystems(1),] vec![StartedSystems(1), StartedSystems(1),]
); );
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_ref.system()) .with_system(wants_ref)
.with_system(wants_ref.system()); .with_system(wants_ref);
stage.run(&mut world); stage.run(&mut world);
assert_eq!(receive_events(&world), vec![StartedSystems(2),]); assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
} }
@ -385,24 +385,24 @@ mod tests {
fn wants_mut(_: Query<&mut usize>) {} fn wants_mut(_: Query<&mut usize>) {}
fn wants_ref(_: Query<&usize>) {} fn wants_ref(_: Query<&usize>) {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut.system()) .with_system(wants_mut)
.with_system(wants_mut.system()); .with_system(wants_mut);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
receive_events(&world), receive_events(&world),
vec![StartedSystems(1), StartedSystems(1),] vec![StartedSystems(1), StartedSystems(1),]
); );
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut.system()) .with_system(wants_mut)
.with_system(wants_ref.system()); .with_system(wants_ref);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
receive_events(&world), receive_events(&world),
vec![StartedSystems(1), StartedSystems(1),] vec![StartedSystems(1), StartedSystems(1),]
); );
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_ref.system()) .with_system(wants_ref)
.with_system(wants_ref.system()); .with_system(wants_ref);
stage.run(&mut world); stage.run(&mut world);
assert_eq!(receive_events(&world), vec![StartedSystems(2),]); assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
let mut world = World::new(); let mut world = World::new();
@ -410,8 +410,8 @@ mod tests {
fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {} fn wants_mut_usize(_: Query<(&mut usize, &f32)>) {}
fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {} fn wants_mut_u32(_: Query<(&mut u32, &f32)>) {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(wants_mut_usize.system()) .with_system(wants_mut_usize)
.with_system(wants_mut_u32.system()); .with_system(wants_mut_u32);
stage.run(&mut world); stage.run(&mut world);
assert_eq!(receive_events(&world), vec![StartedSystems(2),]); assert_eq!(receive_events(&world), vec![StartedSystems(2),]);
} }
@ -426,12 +426,12 @@ mod tests {
} }
fn empty() {} fn empty() {}
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(non_send.system()) .with_system(non_send)
.with_system(non_send.system()) .with_system(non_send)
.with_system(empty.system()) .with_system(empty)
.with_system(empty.system()) .with_system(empty)
.with_system(non_send.system()) .with_system(non_send)
.with_system(non_send.system()); .with_system(non_send);
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
receive_events(&world), receive_events(&world),

View file

@ -1,15 +1,115 @@
pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel}; 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<T> 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::<T>() {
return self == other;
}
false
}
}
pub trait DynHash: DynEq {
fn as_dyn_eq(&self) -> &dyn DynEq;
fn dyn_hash(&self, state: &mut dyn Hasher);
}
impl<T> 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<dyn StageLabel>;
}
pub(crate) type BoxedStageLabel = Box<dyn StageLabel>; pub(crate) type BoxedStageLabel = Box<dyn StageLabel>;
define_label!(SystemLabel); pub trait SystemLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn SystemLabel>;
}
pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>; pub(crate) type BoxedSystemLabel = Box<dyn SystemLabel>;
define_label!(AmbiguitySetLabel); pub trait AmbiguitySetLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn AmbiguitySetLabel>;
}
pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>; pub(crate) type BoxedAmbiguitySetLabel = Box<dyn AmbiguitySetLabel>;
define_label!(RunCriteriaLabel); pub trait RunCriteriaLabel: DynHash + Debug + Send + Sync + 'static {
#[doc(hidden)]
fn dyn_clone(&self) -> Box<dyn RunCriteriaLabel>;
}
pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>; pub(crate) type BoxedRunCriteriaLabel = Box<dyn RunCriteriaLabel>;
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<H: Hasher>(&self, state: &mut H) {
self.dyn_hash(state);
}
}
impl Clone for Box<dyn $trait_name> {
fn clone(&self) -> Self {
self.dyn_clone()
}
}
impl $trait_name for Cow<'static, str> {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(self.clone())
}
}
impl $trait_name for &'static str {
fn dyn_clone(&self) -> Box<dyn $trait_name> {
Box::new(<&str>::clone(self))
}
}
};
}
impl_label!(StageLabel);
impl_label!(SystemLabel);
impl_label!(AmbiguitySetLabel);
impl_label!(RunCriteriaLabel);

View file

@ -22,10 +22,7 @@ pub use system_set::*;
use std::fmt::Debug; use std::fmt::Debug;
use crate::{ use crate::{system::System, world::World};
system::{IntoSystem, System},
world::World,
};
use bevy_utils::HashMap; use bevy_utils::HashMap;
#[derive(Default)] #[derive(Default)]
@ -79,7 +76,7 @@ impl Schedule {
&mut self, &mut self,
system: S, system: S,
) -> &mut Self { ) -> &mut Self {
self.run_criteria.set(Box::new(system.system())); self.run_criteria.set(Box::new(system));
self self
} }

View file

@ -61,12 +61,12 @@ impl Default for BoxedRunCriteria {
} }
impl 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.criteria_system = Some(criteria_system);
self.initialized = false; 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 let Some(ref mut run_criteria) = self.criteria_system {
if !self.initialized { if !self.initialized {
run_criteria.initialize(world); run_criteria.initialize(world);
@ -99,16 +99,16 @@ pub(crate) enum RunCriteriaInner {
} }
pub(crate) struct RunCriteriaContainer { pub(crate) struct RunCriteriaContainer {
pub should_run: ShouldRun, pub(crate) should_run: ShouldRun,
pub inner: RunCriteriaInner, pub(crate) inner: RunCriteriaInner,
pub label: Option<BoxedRunCriteriaLabel>, pub(crate) label: Option<BoxedRunCriteriaLabel>,
pub before: Vec<BoxedRunCriteriaLabel>, pub(crate) before: Vec<BoxedRunCriteriaLabel>,
pub after: Vec<BoxedRunCriteriaLabel>, pub(crate) after: Vec<BoxedRunCriteriaLabel>,
archetype_generation: ArchetypeGeneration, archetype_generation: ArchetypeGeneration,
} }
impl RunCriteriaContainer { impl RunCriteriaContainer {
pub fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self { pub(crate) fn from_descriptor(descriptor: RunCriteriaDescriptor) -> Self {
Self { Self {
should_run: ShouldRun::Yes, should_run: ShouldRun::Yes,
inner: match descriptor.system { 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 { match &self.inner {
RunCriteriaInner::Single(system) => system.name(), RunCriteriaInner::Single(system) => system.name(),
RunCriteriaInner::Piped { 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 { match &mut self.inner {
RunCriteriaInner::Single(system) => system.initialize(world), RunCriteriaInner::Single(system) => system.initialize(world),
RunCriteriaInner::Piped { 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 archetypes = world.archetypes();
let new_generation = archetypes.generation(); let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation); let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
component::ComponentId, component::ComponentId,
prelude::IntoSystem,
schedule::{ schedule::{
graph_utils::{self, DependencyGraphError}, graph_utils::{self, DependencyGraphError},
BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy, BoxedRunCriteria, BoxedRunCriteriaLabel, BoxedSystemLabel, DuplicateLabelStrategy,
@ -8,7 +9,6 @@ use crate::{
RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun, RunCriteriaDescriptor, RunCriteriaDescriptorOrLabel, RunCriteriaInner, ShouldRun,
SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet, SingleThreadedExecutor, SystemContainer, SystemDescriptor, SystemSet,
}, },
system::System,
world::{World, WorldId}, world::{World, WorldId},
}; };
use bevy_utils::{tracing::info, HashMap, HashSet}; use bevy_utils::{tracing::info, HashMap, HashSet};
@ -26,7 +26,7 @@ pub trait Stage: Downcast + Send + Sync {
impl_downcast!(Stage); 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 /// each `SystemStage` will log a report containing
/// pairs of systems with ambiguous execution order. /// pairs of systems with ambiguous execution order.
/// ///
@ -83,6 +83,7 @@ pub struct SystemStage {
uninitialized_parallel: Vec<usize>, uninitialized_parallel: Vec<usize>,
/// Saves the value of the World change_tick during the last tick check /// Saves the value of the World change_tick during the last tick check
last_tick_check: u32, last_tick_check: u32,
/// If true, buffers will be automatically applied at the end of the stage. If false, buffers must be manually applied.
apply_buffers: bool, apply_buffers: bool,
} }
@ -301,16 +302,19 @@ impl SystemStage {
self self
} }
pub fn with_run_criteria<S: System<In = (), Out = ShouldRun>>(mut self, system: S) -> Self { pub fn with_run_criteria<Param, S: IntoSystem<(), ShouldRun, Param>>(
self.set_run_criteria(system); mut self,
system: S,
) -> Self {
self.set_run_criteria(system.system());
self self
} }
pub fn set_run_criteria<S: System<In = (), Out = ShouldRun>>( pub fn set_run_criteria<Param, S: IntoSystem<(), ShouldRun, Param>>(
&mut self, &mut self,
system: S, system: S,
) -> &mut Self { ) -> &mut Self {
self.stage_run_criteria.set(Box::new(system)); self.stage_run_criteria.set(Box::new(system.system()));
self self
} }
@ -919,14 +923,8 @@ mod tests {
move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag) move |world| world.get_resource_mut::<Vec<usize>>().unwrap().push(tag)
} }
// This is silly. https://github.com/bevyengine/bevy/issues/1029 fn make_parallel(tag: usize) -> impl FnMut(ResMut<Vec<usize>>) {
macro_rules! make_parallel { move |mut resource: ResMut<Vec<usize>>| resource.push(tag)
($tag:expr) => {{
fn parallel(mut resource: ResMut<Vec<usize>>) {
resource.push($tag)
}
parallel
}};
} }
fn every_other_time(mut has_ran: Local<bool>) -> ShouldRun { fn every_other_time(mut has_ran: Local<bool>) -> ShouldRun {
@ -944,7 +942,7 @@ mod tests {
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(0).exclusive_system().at_start()) .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(2).exclusive_system().before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end()); .with_system(make_exclusive(3).exclusive_system().at_end());
stage.run(&mut world); stage.run(&mut world);
@ -963,7 +961,7 @@ mod tests {
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_exclusive(2).exclusive_system().before_commands()) .with_system(make_exclusive(2).exclusive_system().before_commands())
.with_system(make_exclusive(3).exclusive_system().at_end()) .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()); .with_system(make_exclusive(0).exclusive_system().at_start());
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
@ -979,10 +977,10 @@ mod tests {
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(2).exclusive_system().before_commands()) .with_system(make_parallel(2).exclusive_system().before_commands())
.with_system(make_parallel!(3).exclusive_system().at_end()) .with_system(make_parallel(3).exclusive_system().at_end())
.with_system(make_parallel!(1).system()) .with_system(make_parallel(1))
.with_system(make_parallel!(0).exclusive_system().at_start()); .with_system(make_parallel(0).exclusive_system().at_start());
stage.run(&mut world); stage.run(&mut world);
assert_eq!( assert_eq!(
*world.get_resource::<Vec<usize>>().unwrap(), *world.get_resource::<Vec<usize>>().unwrap(),
@ -1239,9 +1237,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(1).system().after("0").label("1")) .with_system(make_parallel(1).after("0").label("1"))
.with_system(make_parallel!(2).system().after("1")) .with_system(make_parallel(2).after("1"))
.with_system(make_parallel!(0).system().label("0")); .with_system(make_parallel(0).label("0"));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1256,9 +1254,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(1).system().label("1").before("2")) .with_system(make_parallel(1).label("1").before("2"))
.with_system(make_parallel!(2).system().label("2")) .with_system(make_parallel(2).label("2"))
.with_system(make_parallel!(0).system().before("1")); .with_system(make_parallel(0).before("1"));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1273,11 +1271,11 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(2).system().label("2")) .with_system(make_parallel(2).label("2"))
.with_system(make_parallel!(1).system().after("0").before("2")) .with_system(make_parallel(1).after("0").before("2"))
.with_system(make_parallel!(0).system().label("0")) .with_system(make_parallel(0).label("0"))
.with_system(make_parallel!(4).system().label("4")) .with_system(make_parallel(4).label("4"))
.with_system(make_parallel!(3).system().after("2").before("4")); .with_system(make_parallel(3).after("2").before("4"));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1292,9 +1290,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(1).system().label("first").after("0")) .with_system(make_parallel(1).label("first").after("0"))
.with_system(make_parallel!(2).system().after("first")) .with_system(make_parallel(2).after("first"))
.with_system(make_parallel!(0).system().label("first").label("0")); .with_system(make_parallel(0).label("first").label("0"));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1305,11 +1303,11 @@ mod tests {
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(2).system().after("01").label("2")) .with_system(make_parallel(2).after("01").label("2"))
.with_system(make_parallel!(1).system().label("01").after("0")) .with_system(make_parallel(1).label("01").after("0"))
.with_system(make_parallel!(0).system().label("01").label("0")) .with_system(make_parallel(0).label("01").label("0"))
.with_system(make_parallel!(4).system().label("4")) .with_system(make_parallel(4).label("4"))
.with_system(make_parallel!(3).system().after("2").before("4")); .with_system(make_parallel(3).after("2").before("4"));
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1320,17 +1318,11 @@ mod tests {
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(2).system().label("234").label("2")) .with_system(make_parallel(2).label("234").label("2"))
.with_system(make_parallel!(1).system().before("234").after("0")) .with_system(make_parallel(1).before("234").after("0"))
.with_system(make_parallel!(0).system().label("0")) .with_system(make_parallel(0).label("0"))
.with_system(make_parallel!(4).system().label("234").label("4")) .with_system(make_parallel(4).label("234").label("4"))
.with_system( .with_system(make_parallel(3).label("234").after("2").before("4"));
make_parallel!(3)
.system()
.label("234")
.after("2")
.before("4"),
);
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1346,24 +1338,22 @@ mod tests {
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(
make_parallel!(2) make_parallel(2)
.system()
.label("2") .label("2")
.after("1") .after("1")
.before("3") .before("3")
.before("3"), .before("3"),
) )
.with_system( .with_system(
make_parallel!(1) make_parallel(1)
.system()
.label("1") .label("1")
.after("0") .after("0")
.after("0") .after("0")
.before("2"), .before("2"),
) )
.with_system(make_parallel!(0).system().label("0").before("1")) .with_system(make_parallel(0).label("0").before("1"))
.with_system(make_parallel!(4).system().label("4").after("3")) .with_system(make_parallel(4).label("4").after("3"))
.with_system(make_parallel!(3).system().label("3").after("2").before("4")); .with_system(make_parallel(3).label("3").after("2").before("4"));
stage.run(&mut world); stage.run(&mut world);
for container in stage.parallel.iter() { for container in stage.parallel.iter() {
assert!(container.dependencies().len() <= 1); assert!(container.dependencies().len() <= 1);
@ -1381,14 +1371,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(2).system().label("2")) .with_system(make_parallel(2).label("2"))
.with_system_set( .with_system_set(
SystemSet::new() SystemSet::new()
.with_system(make_parallel!(0).system().label("0")) .with_system(make_parallel(0).label("0"))
.with_system(make_parallel!(4).system().label("4")) .with_system(make_parallel(4).label("4"))
.with_system(make_parallel!(3).system().after("2").before("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.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
stage.run(&mut world); stage.run(&mut world);
@ -1405,12 +1395,11 @@ mod tests {
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(
make_parallel!(0) make_parallel(0)
.system()
.label("0") .label("0")
.with_run_criteria(every_other_time), .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.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -1423,13 +1412,13 @@ mod tests {
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(0).system().before("1")) .with_system(make_parallel(0).before("1"))
.with_system_set( .with_system_set(
SystemSet::new() SystemSet::new()
.with_run_criteria(every_other_time.system()) .with_run_criteria(every_other_time)
.with_system(make_parallel!(1).system().label("1")), .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.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -1444,21 +1433,19 @@ mod tests {
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system_run_criteria(every_other_time.label("every other time")) .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( .with_system(
make_parallel!(1) make_parallel(1)
.system()
.label("1") .label("1")
.with_run_criteria("every other time"), .with_run_criteria("every other time"),
) )
.with_system( .with_system(
make_parallel!(2) make_parallel(2)
.system()
.label("2") .label("2")
.after("1") .after("1")
.with_run_criteria("every other time"), .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.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -1480,34 +1467,26 @@ mod tests {
} }
} }
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(0).system().label("0")) .with_system(make_parallel(0).label("0"))
.with_system( .with_system(
make_parallel!(1) make_parallel(1)
.system()
.label("1") .label("1")
.after("0") .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( .with_system(
make_parallel!(2) make_parallel(2)
.system()
.label("2") .label("2")
.after("1") .after("1")
.with_run_criteria(RunCriteria::pipe("every other time", eot_piped.system())), .with_run_criteria(RunCriteria::pipe("every other time", eot_piped.system())),
) )
.with_system( .with_system(
make_parallel!(3) make_parallel(3)
.system()
.label("3") .label("3")
.after("2") .after("2")
.with_run_criteria("every other time".pipe(eot_piped.system()).label("piped")), .with_run_criteria("every other time".pipe(eot_piped.system()).label("piped")),
) )
.with_system( .with_system(make_parallel(4).after("3").with_run_criteria("piped"));
make_parallel!(4)
.system()
.after("3")
.with_run_criteria("piped"),
);
for _ in 0..4 { for _ in 0..4 {
stage.run(&mut world); stage.run(&mut world);
} }
@ -1523,27 +1502,16 @@ mod tests {
// Discarding extra criteria with matching labels. // Discarding extra criteria with matching labels.
world.get_resource_mut::<Vec<usize>>().unwrap().clear(); world.get_resource_mut::<Vec<usize>>().unwrap().clear();
let mut stage = SystemStage::parallel() let mut stage =
.with_system(make_parallel!(0).system().before("1")) SystemStage::parallel()
.with_system( .with_system(make_parallel(0).before("1"))
make_parallel!(1).system().label("1").with_run_criteria( .with_system(make_parallel(1).label("1").with_run_criteria(
every_other_time every_other_time.label_discard_if_duplicate("every other time"),
.system() ))
.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( .with_system(make_parallel(3).after("2"));
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"));
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
stage.set_executor(Box::new(SingleThreadedExecutor::default())); stage.set_executor(Box::new(SingleThreadedExecutor::default()));
@ -1561,8 +1529,8 @@ mod tests {
fn duplicate_run_criteria_label_panic() { fn duplicate_run_criteria_label_panic() {
let mut world = World::new(); let mut world = World::new();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.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.system().label("every other time")); .with_system_run_criteria(every_other_time.label("every other time"));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1571,8 +1539,7 @@ mod tests {
fn parallel_cycle_1() { fn parallel_cycle_1() {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = let mut stage = SystemStage::parallel().with_system(make_parallel(0).label("0").after("0"));
SystemStage::parallel().with_system(make_parallel!(0).system().label("0").after("0"));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1582,8 +1549,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(make_parallel!(0).system().label("0").after("1")) .with_system(make_parallel(0).label("0").after("1"))
.with_system(make_parallel!(1).system().label("1").after("0")); .with_system(make_parallel(1).label("1").after("0"));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1594,9 +1561,9 @@ mod tests {
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() 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().after("0").before("2")) .with_system(make_parallel(1).after("0").before("2"))
.with_system(make_parallel!(2).system().label("2").before("0")); .with_system(make_parallel(2).label("2").before("0"));
stage.run(&mut world); stage.run(&mut world);
} }
@ -1625,21 +1592,21 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(empty.system().label("0")) .with_system(empty.label("0"))
.with_system(empty.system().label("1").after("0")) .with_system(empty.label("1").after("0"))
.with_system(empty.system().label("2")) .with_system(empty.label("2"))
.with_system(empty.system().label("3").after("2").before("4")) .with_system(empty.label("3").after("2").before("4"))
.with_system(empty.system().label("4")); .with_system(empty.label("4"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
assert_eq!(find_ambiguities(&stage.parallel).len(), 0); assert_eq!(find_ambiguities(&stage.parallel).len(), 0);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(empty.system().label("0")) .with_system(empty.label("0"))
.with_system(component.system().label("1").after("0")) .with_system(component.label("1").after("0"))
.with_system(empty.system().label("2")) .with_system(empty.label("2"))
.with_system(empty.system().label("3").after("2").before("4")) .with_system(empty.label("3").after("2").before("4"))
.with_system(component.system().label("4")); .with_system(component.label("4"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1650,11 +1617,11 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(empty.system().label("0")) .with_system(empty.label("0"))
.with_system(resource.system().label("1").after("0")) .with_system(resource.label("1").after("0"))
.with_system(empty.system().label("2")) .with_system(empty.label("2"))
.with_system(empty.system().label("3").after("2").before("4")) .with_system(empty.label("3").after("2").before("4"))
.with_system(resource.system().label("4")); .with_system(resource.label("4"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1665,21 +1632,21 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(empty.system().label("0")) .with_system(empty.label("0"))
.with_system(resource.system().label("1").after("0")) .with_system(resource.label("1").after("0"))
.with_system(empty.system().label("2")) .with_system(empty.label("2"))
.with_system(empty.system().label("3").after("2").before("4")) .with_system(empty.label("3").after("2").before("4"))
.with_system(component.system().label("4")); .with_system(component.label("4"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
assert_eq!(find_ambiguities(&stage.parallel).len(), 0); assert_eq!(find_ambiguities(&stage.parallel).len(), 0);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0")) .with_system(component.label("0"))
.with_system(resource.system().label("1").after("0")) .with_system(resource.label("1").after("0"))
.with_system(empty.system().label("2")) .with_system(empty.label("2"))
.with_system(component.system().label("3").after("2").before("4")) .with_system(component.label("3").after("2").before("4"))
.with_system(resource.system().label("4")); .with_system(resource.label("4"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1694,17 +1661,11 @@ mod tests {
assert_eq!(ambiguities.len(), 2); assert_eq!(ambiguities.len(), 2);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0")) .with_system(component.label("0"))
.with_system( .with_system(resource.label("1").after("0").in_ambiguity_set("a"))
resource .with_system(empty.label("2"))
.system() .with_system(component.label("3").after("2").before("4"))
.label("1") .with_system(resource.label("4").in_ambiguity_set("a"));
.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"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1715,9 +1676,9 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0").before("2")) .with_system(component.label("0").before("2"))
.with_system(component.system().label("1").before("2")) .with_system(component.label("1").before("2"))
.with_system(component.system().label("2")); .with_system(component.label("2"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1728,9 +1689,9 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0")) .with_system(component.label("0"))
.with_system(component.system().label("1").after("0")) .with_system(component.label("1").after("0"))
.with_system(component.system().label("2").after("0")); .with_system(component.label("2").after("0"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1741,10 +1702,10 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0").before("1").before("2")) .with_system(component.label("0").before("1").before("2"))
.with_system(component.system().label("1")) .with_system(component.label("1"))
.with_system(component.system().label("2")) .with_system(component.label("2"))
.with_system(component.system().label("3").after("1").after("2")); .with_system(component.label("3").after("1").after("2"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1755,20 +1716,20 @@ mod tests {
assert_eq!(ambiguities.len(), 1); assert_eq!(ambiguities.len(), 1);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0").before("1").before("2")) .with_system(component.label("0").before("1").before("2"))
.with_system(component.system().label("1").in_ambiguity_set("a")) .with_system(component.label("1").in_ambiguity_set("a"))
.with_system(component.system().label("2").in_ambiguity_set("a")) .with_system(component.label("2").in_ambiguity_set("a"))
.with_system(component.system().label("3").after("1").after("2")); .with_system(component.label("3").after("1").after("2"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
assert_eq!(ambiguities.len(), 0); assert_eq!(ambiguities.len(), 0);
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(component.system().label("0").before("1").before("2")) .with_system(component.label("0").before("1").before("2"))
.with_system(component.system().label("1").in_ambiguity_set("a")) .with_system(component.label("1").in_ambiguity_set("a"))
.with_system(component.system().label("2").in_ambiguity_set("b")) .with_system(component.label("2").in_ambiguity_set("b"))
.with_system(component.system().label("3").after("1").after("2")); .with_system(component.label("3").after("1").after("2"));
stage.initialize_systems(&mut world); stage.initialize_systems(&mut world);
stage.rebuild_orders_and_dependencies(); stage.rebuild_orders_and_dependencies();
let ambiguities = find_ambiguities_first_labels(&stage.parallel); let ambiguities = find_ambiguities_first_labels(&stage.parallel);
@ -1781,20 +1742,18 @@ mod tests {
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(
component component
.system()
.label("0") .label("0")
.before("1") .before("1")
.before("2") .before("2")
.before("3") .before("3")
.before("4"), .before("4"),
) )
.with_system(component.system().label("1")) .with_system(component.label("1"))
.with_system(component.system().label("2")) .with_system(component.label("2"))
.with_system(component.system().label("3")) .with_system(component.label("3"))
.with_system(component.system().label("4")) .with_system(component.label("4"))
.with_system( .with_system(
component component
.system()
.label("5") .label("5")
.after("1") .after("1")
.after("2") .after("2")
@ -1833,20 +1792,18 @@ mod tests {
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(
component component
.system()
.label("0") .label("0")
.before("1") .before("1")
.before("2") .before("2")
.before("3") .before("3")
.before("4"), .before("4"),
) )
.with_system(component.system().label("1").in_ambiguity_set("a")) .with_system(component.label("1").in_ambiguity_set("a"))
.with_system(component.system().label("2").in_ambiguity_set("a")) .with_system(component.label("2").in_ambiguity_set("a"))
.with_system(component.system().label("3").in_ambiguity_set("a")) .with_system(component.label("3").in_ambiguity_set("a"))
.with_system(component.system().label("4").in_ambiguity_set("a")) .with_system(component.label("4").in_ambiguity_set("a"))
.with_system( .with_system(
component component
.system()
.label("5") .label("5")
.after("1") .after("1")
.after("2") .after("2")
@ -1861,26 +1818,23 @@ mod tests {
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system( .with_system(
component component
.system()
.label("0") .label("0")
.before("1") .before("1")
.before("2") .before("2")
.before("3") .before("3")
.before("4"), .before("4"),
) )
.with_system(component.system().label("1").in_ambiguity_set("a")) .with_system(component.label("1").in_ambiguity_set("a"))
.with_system(component.system().label("2").in_ambiguity_set("a")) .with_system(component.label("2").in_ambiguity_set("a"))
.with_system( .with_system(
component component
.system()
.label("3") .label("3")
.in_ambiguity_set("a") .in_ambiguity_set("a")
.in_ambiguity_set("b"), .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( .with_system(
component component
.system()
.label("5") .label("5")
.after("1") .after("1")
.after("2") .after("2")
@ -2011,7 +1965,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(0_usize); 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(); let entity = world.spawn().insert_bundle(()).id();
stage.run(&mut world); stage.run(&mut world);
@ -2034,7 +1988,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(0_usize); world.insert_resource(0_usize);
let mut stage = SystemStage::parallel(); 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(); let entity = world.spawn().insert_bundle(()).id();
stage.run(&mut world); stage.run(&mut world);
@ -2056,7 +2010,7 @@ mod tests {
let mut stage = SystemStage::parallel(); let mut stage = SystemStage::parallel();
fn work() {} fn work() {}
stage.add_system(work.system()); stage.add_system(work);
// Overflow twice // Overflow twice
for _ in 0..10 { for _ in 0..10 {
@ -2134,11 +2088,11 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(spawn_entity.system().label("spawn")) .with_system(spawn_entity.label("spawn"))
.with_system_set( .with_system_set(
SystemSet::new() SystemSet::new()
.with_run_criteria(even_number_of_entities_critiera.system()) .with_run_criteria(even_number_of_entities_critiera)
.with_system(count_entities.system().before("spawn")), .with_system(count_entities.before("spawn")),
); );
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);
@ -2169,10 +2123,10 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::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() let mut stage_count = SystemStage::parallel()
.with_run_criteria(even_number_of_entities_critiera.system()) .with_run_criteria(even_number_of_entities_critiera)
.with_system(count_entities.system()); .with_system(count_entities);
stage_count.run(&mut world); stage_count.run(&mut world);
stage_spawn.run(&mut world); stage_spawn.run(&mut world);
stage_count.run(&mut world); stage_count.run(&mut world);

View file

@ -4,7 +4,7 @@ use crate::{
RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun, RunCriteriaDescriptor, RunCriteriaDescriptorCoercion, RunCriteriaLabel, ShouldRun,
SystemSet, SystemSet,
}, },
system::{In, IntoChainSystem, IntoSystem, Local, Res, ResMut}, system::{ConfigurableSystem, In, IntoChainSystem, Local, Res, ResMut},
}; };
use std::{any::TypeId, fmt::Debug, hash::Hash}; use std::{any::TypeId, fmt::Debug, hash::Hash};
use thiserror::Error; use thiserror::Error;
@ -97,9 +97,8 @@ where
(|state: Res<State<T>>, pred: Local<Option<T>>| { (|state: Res<State<T>>, pred: Local<Option<T>>| {
state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none() state.stack.last().unwrap() == pred.as_ref().unwrap() && state.transition.is_none()
}) })
.system()
.config(|(_, pred)| *pred = Some(Some(s.clone()))) .config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Update.into_label(s)) .label_discard_if_duplicate(StateCallback::Update.into_label(s))
} }
@ -118,9 +117,8 @@ where
Some(_) => false, Some(_) => false,
None => *is_inactive, None => *is_inactive,
}) })
.system()
.config(|(_, _, pred)| *pred = Some(Some(s.clone()))) .config(|(_, _, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(s)) .label_discard_if_duplicate(StateCallback::InactiveUpdate.into_label(s))
} }
@ -151,9 +149,8 @@ where
Some(_) => false, Some(_) => false,
None => *is_in_stack, None => *is_in_stack,
}) })
.system()
.config(|(_, _, pred)| *pred = Some(Some(s.clone()))) .config(|(_, _, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(s)) .label_discard_if_duplicate(StateCallback::InStackUpdate.into_label(s))
} }
@ -171,9 +168,8 @@ where
_ => false, _ => false,
}) })
}) })
.system()
.config(|(_, pred)| *pred = Some(Some(s.clone()))) .config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Enter.into_label(s)) .label_discard_if_duplicate(StateCallback::Enter.into_label(s))
} }
@ -189,9 +185,8 @@ where
_ => false, _ => false,
}) })
}) })
.system()
.config(|(_, pred)| *pred = Some(Some(s.clone()))) .config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Exit.into_label(s)) .label_discard_if_duplicate(StateCallback::Exit.into_label(s))
} }
@ -206,9 +201,8 @@ where
_ => false, _ => false,
}) })
}) })
.system()
.config(|(_, pred)| *pred = Some(Some(s.clone()))) .config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Pause.into_label(s)) .label_discard_if_duplicate(StateCallback::Pause.into_label(s))
} }
@ -223,9 +217,8 @@ where
_ => false, _ => false,
}) })
}) })
.system()
.config(|(_, pred)| *pred = Some(Some(s.clone()))) .config(|(_, pred)| *pred = Some(Some(s.clone())))
.chain(should_run_adapter::<T>.system()) .chain(should_run_adapter::<T>)
.after(DriverLabel::of::<T>()) .after(DriverLabel::of::<T>())
.label_discard_if_duplicate(StateCallback::Resume.into_label(s)) .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 /// Important note: this set must be inserted **before** all other state-dependant sets to work
/// properly! /// properly!
pub fn get_driver() -> SystemSet { pub fn get_driver() -> SystemSet {
SystemSet::default() SystemSet::default().with_run_criteria(state_cleaner::<T>.label(DriverLabel::of::<T>()))
.with_run_criteria(state_cleaner::<T>.system().label(DriverLabel::of::<T>()))
} }
pub fn new(initial: T) -> Self { pub fn new(initial: T) -> Self {
@ -516,81 +508,63 @@ mod test {
stage stage
.add_system_set( .add_system_set(
State::on_enter_set(MyState::S1) State::on_enter_set(MyState::S1)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("startup")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("startup")),
)
.add_system_set(
State::on_update_set(MyState::S1).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S1");
s.overwrite_replace(MyState::S2).unwrap();
})
.system(),
),
) )
.add_system_set(State::on_update_set(MyState::S1).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S1");
s.overwrite_replace(MyState::S2).unwrap();
},
))
.add_system_set( .add_system_set(
State::on_enter_set(MyState::S2) State::on_enter_set(MyState::S2)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("enter S2")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S2")),
)
.add_system_set(
State::on_update_set(MyState::S2).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S2");
s.overwrite_replace(MyState::S3).unwrap();
})
.system(),
),
) )
.add_system_set(State::on_update_set(MyState::S2).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S2");
s.overwrite_replace(MyState::S3).unwrap();
},
))
.add_system_set( .add_system_set(
State::on_exit_set(MyState::S2) State::on_exit_set(MyState::S2)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("exit S2")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S2")),
) )
.add_system_set( .add_system_set(
State::on_enter_set(MyState::S3) State::on_enter_set(MyState::S3)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("enter S3")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("enter S3")),
)
.add_system_set(
State::on_update_set(MyState::S3).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S3");
s.overwrite_push(MyState::S4).unwrap();
})
.system(),
),
) )
.add_system_set(State::on_update_set(MyState::S3).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S3");
s.overwrite_push(MyState::S4).unwrap();
},
))
.add_system_set( .add_system_set(
State::on_pause_set(MyState::S3) State::on_pause_set(MyState::S3)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("pause S3")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("pause S3")),
)
.add_system_set(
State::on_update_set(MyState::S4).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
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<Vec<&'static str>>| r.push("inactive S4"))
.system()
.label("inactive s4"),
),
) )
.add_system_set(State::on_update_set(MyState::S4).with_system(
|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
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<Vec<&'static str>>| r.push("inactive S4")).label("inactive s4"),
))
.add_system_set( .add_system_set(
State::on_update_set(MyState::S5).with_system( State::on_update_set(MyState::S5).with_system(
(|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| { (|mut r: ResMut<Vec<&'static str>>, mut s: ResMut<State<MyState>>| {
r.push("update S5"); r.push("update S5");
s.overwrite_push(MyState::S6).unwrap(); s.overwrite_push(MyState::S6).unwrap();
}) })
.system()
.after("inactive s4"), .after("inactive s4"),
), ),
) )
.add_system_set( .add_system_set(
State::on_inactive_update_set(MyState::S5).with_system( State::on_inactive_update_set(MyState::S5).with_system(
(|mut r: ResMut<Vec<&'static str>>| r.push("inactive S5")) (|mut r: ResMut<Vec<&'static str>>| r.push("inactive S5"))
.system()
.label("inactive s5") .label("inactive s5")
.after("inactive s4"), .after("inactive s4"),
), ),
@ -601,17 +575,16 @@ mod test {
r.push("update S6"); r.push("update S6");
s.overwrite_push(MyState::Final).unwrap(); s.overwrite_push(MyState::Final).unwrap();
}) })
.system()
.after("inactive s5"), .after("inactive s5"),
), ),
) )
.add_system_set( .add_system_set(
State::on_resume_set(MyState::S4) State::on_resume_set(MyState::S4)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("resume S4")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("resume S4")),
) )
.add_system_set( .add_system_set(
State::on_exit_set(MyState::S5) State::on_exit_set(MyState::S5)
.with_system((|mut r: ResMut<Vec<&'static str>>| r.push("exit S4")).system()), .with_system(|mut r: ResMut<Vec<&'static str>>| r.push("exit S4")),
); );
const EXPECTED: &[&str] = &[ const EXPECTED: &[&str] = &[
@ -671,7 +644,7 @@ mod test {
world.insert_resource(State::new(AppState::Main)); world.insert_resource(State::new(AppState::Main));
world.insert_resource(false); world.insert_resource(false);
world.insert_resource("control"); 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); stage.run(&mut world);
assert!(*world.get_resource::<bool>().unwrap(), "after control"); assert!(*world.get_resource::<bool>().unwrap(), "after control");
@ -679,7 +652,7 @@ mod test {
world.insert_resource("test"); world.insert_resource("test");
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system_set(State::<AppState>::get_driver()) .with_system_set(State::<AppState>::get_driver())
.with_system(should_run_once.system()); .with_system(should_run_once);
stage.run(&mut world); stage.run(&mut world);
assert!(*world.get_resource::<bool>().unwrap(), "after test"); assert!(*world.get_resource::<bool>().unwrap(), "after test");
} }

View file

@ -36,7 +36,7 @@ pub(super) struct ExclusiveSystemContainer {
} }
impl ExclusiveSystemContainer { impl ExclusiveSystemContainer {
pub fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self { pub(super) fn from_descriptor(descriptor: ExclusiveSystemDescriptor) -> Self {
ExclusiveSystemContainer { ExclusiveSystemContainer {
system: descriptor.system, system: descriptor.system,
run_criteria_index: None, run_criteria_index: None,
@ -49,7 +49,7 @@ impl ExclusiveSystemContainer {
} }
} }
pub fn system_mut(&mut self) -> &mut Box<dyn ExclusiveSystem> { pub(super) fn system_mut(&mut self) -> &mut Box<dyn ExclusiveSystem> {
&mut self.system &mut self.system
} }
} }

View file

@ -30,8 +30,8 @@ use crate::{
/// struct Something; /// struct Something;
/// ///
/// SystemStage::parallel() /// SystemStage::parallel()
/// .with_system(do_something.system().label(Something)) /// .with_system(do_something.label(Something))
/// .with_system(do_the_other_thing.system().after(Something)) /// .with_system(do_the_other_thing.after(Something))
/// .with_system(do_something_else.exclusive_system().at_end()); /// .with_system(do_something_else.exclusive_system().at_end());
/// ``` /// ```
pub enum SystemDescriptor { pub enum SystemDescriptor {
@ -60,6 +60,12 @@ where
} }
} }
impl IntoSystemDescriptor<()> for SystemDescriptor {
fn into_descriptor(self) -> SystemDescriptor {
self
}
}
impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> { impl IntoSystemDescriptor<()> for BoxedSystem<(), ()> {
fn into_descriptor(self) -> SystemDescriptor { fn into_descriptor(self) -> SystemDescriptor {
new_parallel_descriptor(self).into_descriptor() new_parallel_descriptor(self).into_descriptor()

View file

@ -267,9 +267,13 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::BlobVec; use super::BlobVec;
use crate::component::TypeInfo;
use std::{alloc::Layout, cell::RefCell, rc::Rc}; 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<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
/// # Safety /// # Safety
/// ///
/// `blob_vec` must have a layout that matches Layout::new::<T>() /// `blob_vec` must have a layout that matches Layout::new::<T>()
@ -300,7 +304,7 @@ mod tests {
#[test] #[test]
fn resize_test() { fn resize_test() {
let item_layout = Layout::new::<usize>(); let item_layout = Layout::new::<usize>();
let drop = TypeInfo::drop_ptr::<usize>; let drop = drop_ptr::<usize>;
let mut blob_vec = BlobVec::new(item_layout, drop, 64); let mut blob_vec = BlobVec::new(item_layout, drop, 64);
unsafe { unsafe {
for i in 0..1_000 { for i in 0..1_000 {
@ -330,7 +334,7 @@ mod tests {
let drop_counter = Rc::new(RefCell::new(0)); let drop_counter = Rc::new(RefCell::new(0));
{ {
let item_layout = Layout::new::<Foo>(); let item_layout = Layout::new::<Foo>();
let drop = TypeInfo::drop_ptr::<Foo>; let drop = drop_ptr::<Foo>;
let mut blob_vec = BlobVec::new(item_layout, drop, 2); let mut blob_vec = BlobVec::new(item_layout, drop, 2);
assert_eq!(blob_vec.capacity(), 2); assert_eq!(blob_vec.capacity(), 2);
unsafe { unsafe {
@ -390,7 +394,7 @@ mod tests {
#[test] #[test]
fn blob_vec_drop_empty_capacity() { fn blob_vec_drop_empty_capacity() {
let item_layout = Layout::new::<Foo>(); let item_layout = Layout::new::<Foo>();
let drop = TypeInfo::drop_ptr::<Foo>; let drop = drop_ptr::<Foo>;
let _ = BlobVec::new(item_layout, drop, 0); let _ = BlobVec::new(item_layout, drop, 0);
} }
} }

View file

@ -522,17 +522,12 @@ impl IndexMut<TableId> for Tables {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{component::Components, entity::Entity, storage::Table};
component::{Components, TypeInfo},
entity::Entity,
storage::Table,
};
#[test] #[test]
fn table() { fn table() {
let mut components = Components::default(); let mut components = Components::default();
let type_info = TypeInfo::of::<usize>(); let component_id = components.get_or_insert_id::<usize>();
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info);
let columns = &[component_id]; let columns = &[component_id];
let mut table = Table::with_capacity(0, columns.len()); let mut table = Table::with_capacity(0, columns.len());
table.add_column(components.get_info(component_id).unwrap()); table.add_column(components.get_info(component_id).unwrap());

View file

@ -6,7 +6,7 @@ use crate::{
entity::{Entities, Entity}, entity::{Entities, Entity},
world::World, world::World,
}; };
use bevy_utils::tracing::debug; use bevy_utils::tracing::{error, warn};
pub use command_queue::CommandQueue; pub use command_queue::CommandQueue;
use std::marker::PhantomData; 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`]. /// 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, queue: &'s mut CommandQueue,
entities: &'w Entities, 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. /// Create a new `Commands` from a queue and a world.
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self { pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
Self { Self {
@ -50,7 +50,7 @@ impl<'s, 'w> Commands<'s, 'w> {
/// } /// }
/// # example_system.system(); /// # 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(); let entity = self.entities.reserve_entity();
EntityCommands { EntityCommands {
entity, 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 }); self.add(GetOrSpawn { entity });
EntityCommands { EntityCommands {
entity, entity,
@ -66,9 +73,8 @@ impl<'s, 'w> Commands<'s, 'w> {
} }
} }
// TODO: this is a hack to work around the "multiple worlds" limitations: /// Spawns a [Bundle] without pre-allocating an [Entity]. The [Entity] will be allocated when
// Right now Commands must allocate entities from their "scheduled" world, but Commands might be applied to other worlds, /// this [Command] is applied.
// such as the "render world"
pub fn spawn_and_forget(&mut self, bundle: impl Bundle) { pub fn spawn_and_forget(&mut self, bundle: impl Bundle) {
self.queue.push(Spawn { bundle }) self.queue.push(Spawn { bundle })
} }
@ -113,7 +119,7 @@ impl<'s, 'w> Commands<'s, 'w> {
/// } /// }
/// # example_system.system(); /// # example_system.system();
/// ``` /// ```
pub fn spawn_bundle<T: 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(); let mut e = self.spawn();
e.insert_bundle(bundle); e.insert_bundle(bundle);
e e
@ -138,7 +144,13 @@ impl<'s, 'w> Commands<'s, 'w> {
/// } /// }
/// # example_system.system(); /// # 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 { EntityCommands {
entity, entity,
commands: self, commands: self,
@ -155,6 +167,23 @@ impl<'s, 'w> Commands<'s, 'w> {
self.queue.push(SpawnBatch { bundles_iter }); 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<I, B>(&mut self, bundles_iter: I)
where
I: IntoIterator + Send + Sync + 'static,
I::IntoIter: Iterator<Item = (Entity, B)>,
B: Bundle,
{
self.queue.push(InsertOrSpawnBatch { bundles_iter });
}
/// See [`World::insert_resource`]. /// See [`World::insert_resource`].
pub fn insert_resource<T: Component>(&mut self, resource: T) { pub fn insert_resource<T: Component>(&mut self, resource: T) {
self.queue.push(InsertResource { resource }) 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`]. /// 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, 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. /// Retrieves the current entity's unique [`Entity`] id.
#[inline] #[inline]
pub fn id(&self) -> Entity { pub fn id(&self) -> Entity {
@ -267,7 +296,7 @@ impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
} }
/// Returns the underlying `[Commands]`. /// Returns the underlying `[Commands]`.
pub fn commands(&mut self) -> &mut Commands<'s, 'w> { pub fn commands(&mut self) -> &mut Commands<'w, 's> {
self.commands self.commands
} }
} }
@ -314,6 +343,32 @@ where
} }
} }
pub struct InsertOrSpawnBatch<I, B>
where
I: IntoIterator + Send + Sync + 'static,
B: Bundle,
I::IntoIter: Iterator<Item = (Entity, B)>,
{
pub bundles_iter: I,
}
impl<I, B> Command for InsertOrSpawnBatch<I, B>
where
I: IntoIterator + Send + Sync + 'static,
B: Bundle,
I::IntoIter: Iterator<Item = (Entity, B)>,
{
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::<B>(),
invalid_entities
);
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct Despawn { pub struct Despawn {
pub entity: Entity, pub entity: Entity,
@ -322,7 +377,9 @@ pub struct Despawn {
impl Command for Despawn { impl Command for Despawn {
fn write(self, world: &mut World) { fn write(self, world: &mut World) {
if !world.despawn(self.entity) { 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, T: Bundle + 'static,
{ {
fn write(self, world: &mut World) { 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::<T>(), self.entity);
}
} }
} }
@ -352,7 +415,13 @@ where
T: Component, T: Component,
{ {
fn write(self, world: &mut World) { 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::<T>(), self.entity);
}
} }
} }

View file

@ -96,6 +96,7 @@ impl ExclusiveSystem for ExclusiveSystemCoerced {
for archetype in archetypes.archetypes[archetype_index_range].iter() { for archetype in archetypes.archetypes[archetype_index_range].iter() {
self.system.new_archetype(archetype); self.system.new_archetype(archetype);
} }
self.system.run((), world); self.system.run((), world);
self.system.apply_buffers(world); self.system.apply_buffers(world);
} }
@ -127,7 +128,7 @@ mod tests {
entity::Entity, entity::Entity,
query::With, query::With,
schedule::{Stage, SystemStage}, schedule::{Stage, SystemStage},
system::{Commands, IntoExclusiveSystem, IntoSystem, Query, ResMut}, system::{Commands, IntoExclusiveSystem, Query, ResMut},
world::World, world::World,
}; };
#[test] #[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.spawn().insert(0.0f32);
world.insert_resource(0usize); world.insert_resource(0usize);
stage.run(&mut world); stage.run(&mut world);
@ -175,7 +176,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.insert_resource(Vec::<usize>::new()); world.insert_resource(Vec::<usize>::new());
let mut stage = SystemStage::parallel() let mut stage = SystemStage::parallel()
.with_system(spawn_entity.system()) .with_system(spawn_entity)
.with_system(count_entities.exclusive_system()); .with_system(count_entities.exclusive_system());
stage.run(&mut world); stage.run(&mut world);
stage.run(&mut world); stage.run(&mut world);

View file

@ -24,7 +24,7 @@ pub struct SystemMeta {
} }
impl SystemMeta { impl SystemMeta {
pub fn new<T>() -> Self { fn new<T>() -> Self {
Self { Self {
name: std::any::type_name::<T>().into(), name: std::any::type_name::<T>().into(),
archetype_component_access: Access::default(), archetype_component_access: Access::default(),
@ -87,10 +87,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only. /// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
#[inline] #[inline]
pub fn get<'s, 'w>( pub fn get<'w, 's>(
&'s mut self, &'s mut self,
world: &'w World, world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item ) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item
where where
Param::Fetch: ReadOnlySystemParamFetch, Param::Fetch: ReadOnlySystemParamFetch,
{ {
@ -101,10 +101,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the mutable [`SystemParam`] values. /// Retrieve the mutable [`SystemParam`] values.
#[inline] #[inline]
pub fn get_mut<'s, 'w>( pub fn get_mut<'w, 's>(
&'s mut self, &'s mut self,
world: &'w mut World, world: &'w mut World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item { ) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
self.validate_world_and_update_archetypes(world); self.validate_world_and_update_archetypes(world);
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with. // SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
unsafe { self.get_unchecked_manual(world) } unsafe { self.get_unchecked_manual(world) }
@ -145,10 +145,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was /// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
/// created with. /// created with.
#[inline] #[inline]
pub unsafe fn get_unchecked_manual<'s, 'w>( pub unsafe fn get_unchecked_manual<'w, 's>(
&'s mut self, &'s mut self,
world: &'w World, world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item { ) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
let change_tick = world.increment_change_tick(); let change_tick = world.increment_change_tick();
let param = <Param::Fetch as SystemParamFetch>::get_param( let param = <Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state, &mut self.param_state,
@ -228,7 +228,7 @@ pub struct InputMarker;
/// ///
/// You get this by calling [`IntoSystem::system`] on a function that only accepts /// 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 /// [`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<In, Out, Param, Marker, F> pub struct FunctionSystem<In, Out, Param, Marker, F>
where where
Param: SystemParam, Param: SystemParam,
@ -254,7 +254,7 @@ impl<In, Out, Param: SystemParam, Marker, F> FunctionSystem<In, Out, Param, Mark
/// fn local_is_42(local: Local<usize>) { /// fn local_is_42(local: Local<usize>) {
/// assert_eq!(*local, 42); /// 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.initialize(world);
/// system.run((), world); /// system.run((), world);
/// ``` /// ```

View file

@ -24,11 +24,11 @@ mod tests {
bundle::Bundles, bundle::Bundles,
component::Components, component::Components,
entity::{Entities, Entity}, entity::{Entities, Entity},
query::{Added, Changed, Or, With, Without}, query::{Added, Changed, Or, QueryState, With, Without},
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
system::{ system::{
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet, ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, Query,
RemovedComponents, Res, ResMut, System, SystemState, QuerySet, RemovedComponents, Res, ResMut, System, SystemState,
}, },
world::{FromWorld, World}, world::{FromWorld, World},
}; };
@ -60,7 +60,7 @@ mod tests {
system.run((), &mut world); system.run((), &mut world);
} }
fn run_system<S: System<In = (), Out = ()>>(world: &mut World, system: S) { fn run_system<Param, S: IntoSystem<(), (), Param>>(world: &mut World, system: S) {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
let mut update = SystemStage::parallel(); let mut update = SystemStage::parallel();
update.add_system(system); update.add_system(system);
@ -121,7 +121,7 @@ mod tests {
world.spawn().insert_bundle((A, C)); world.spawn().insert_bundle((A, C));
world.spawn().insert_bundle((A, D)); world.spawn().insert_bundle((A, D));
run_system(&mut world, query_system.system()); run_system(&mut world, query_system);
assert!(*world.get_resource::<bool>().unwrap(), "system ran"); assert!(*world.get_resource::<bool>().unwrap(), "system ran");
} }
@ -131,9 +131,9 @@ mod tests {
// Regression test for issue #762 // Regression test for issue #762
fn query_system( fn query_system(
mut ran: ResMut<bool>, mut ran: ResMut<bool>,
set: QuerySet<( mut set: QuerySet<(
Query<(), Or<(Changed<A>, Changed<B>)>>, QueryState<(), Or<(Changed<A>, Changed<B>)>>,
Query<(), Or<(Added<A>, Added<B>)>>, QueryState<(), Or<(Added<A>, Added<B>)>>,
)>, )>,
) { ) {
let changed = set.q0().iter().count(); let changed = set.q0().iter().count();
@ -149,7 +149,7 @@ mod tests {
world.insert_resource(false); world.insert_resource(false);
world.spawn().insert_bundle((A, B)); world.spawn().insert_bundle((A, B));
run_system(&mut world, query_system.system()); run_system(&mut world, query_system);
assert!(*world.get_resource::<bool>().unwrap(), "system ran"); assert!(*world.get_resource::<bool>().unwrap(), "system ran");
} }
@ -179,7 +179,7 @@ mod tests {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
let mut update = SystemStage::parallel(); 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("update", update);
schedule.add_stage( schedule.add_stage(
"clear_trackers", "clear_trackers",
@ -206,7 +206,7 @@ mod tests {
fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {} fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
let mut world = World::default(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
@ -214,7 +214,7 @@ mod tests {
fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {} fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {}
let mut world = World::default(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
@ -222,7 +222,7 @@ mod tests {
fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {} fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {}
let mut world = World::default(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
@ -231,32 +231,36 @@ mod tests {
fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {} fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
let mut world = World::default(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
fn query_set_system() { 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(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn conflicting_query_with_query_set_system() { 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(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn conflicting_query_sets_system() { 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(); let mut world = World::default();
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[derive(Default)] #[derive(Default)]
@ -264,39 +268,39 @@ mod tests {
_buffer: Vec<u8>, _buffer: Vec<u8>,
} }
fn test_for_conflicting_resources<S: System<In = (), Out = ()>>(sys: S) { fn test_for_conflicting_resources<Param, S: IntoSystem<(), (), Param>>(sys: S) {
let mut world = World::default(); let mut world = World::default();
world.insert_resource(BufferRes::default()); world.insert_resource(BufferRes::default());
world.insert_resource(A); world.insert_resource(A);
world.insert_resource(B); world.insert_resource(B);
run_system(&mut world, sys.system()); run_system(&mut world, sys);
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn conflicting_system_resources() { fn conflicting_system_resources() {
fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {} fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {}
test_for_conflicting_resources(sys.system()) test_for_conflicting_resources(sys)
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn conflicting_system_resources_reverse_order() { fn conflicting_system_resources_reverse_order() {
fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {} fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {}
test_for_conflicting_resources(sys.system()) test_for_conflicting_resources(sys)
} }
#[test] #[test]
#[should_panic] #[should_panic]
fn conflicting_system_resources_multiple_mutable() { fn conflicting_system_resources_multiple_mutable() {
fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {} fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {}
test_for_conflicting_resources(sys.system()) test_for_conflicting_resources(sys)
} }
#[test] #[test]
fn nonconflicting_system_resources() { fn nonconflicting_system_resources() {
fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<A>) {} fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<A>) {}
test_for_conflicting_resources(sys.system()) test_for_conflicting_resources(sys)
} }
#[test] #[test]
@ -321,12 +325,54 @@ mod tests {
*modified = true; *modified = true;
} }
run_system(&mut world, sys.system()); run_system(&mut world, sys);
// ensure the system actually ran // ensure the system actually ran
assert!(*world.get_resource::<bool>().unwrap()); assert!(*world.get_resource::<bool>().unwrap());
} }
#[test]
fn non_send_option_system() {
let mut world = World::default();
world.insert_resource(false);
struct NotSend1(std::rc::Rc<i32>);
struct NotSend2(std::rc::Rc<i32>);
world.insert_non_send(NotSend1(std::rc::Rc::new(0)));
fn sys(
op: Option<NonSend<NotSend1>>,
mut _op2: Option<NonSendMut<NotSend2>>,
mut run: ResMut<bool>,
) {
op.expect("NonSend should exist");
*run = true;
}
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.get_resource::<bool>().unwrap());
}
#[test]
fn non_send_system() {
let mut world = World::default();
world.insert_resource(false);
struct NotSend1(std::rc::Rc<i32>);
struct NotSend2(std::rc::Rc<i32>);
world.insert_non_send(NotSend1(std::rc::Rc::new(1)));
world.insert_non_send(NotSend2(std::rc::Rc::new(2)));
fn sys(_op: NonSend<NotSend1>, mut _op2: NonSendMut<NotSend2>, mut run: ResMut<bool>) {
*run = true;
}
run_system(&mut world, sys);
assert!(*world.get_resource::<bool>().unwrap());
}
#[test] #[test]
fn remove_tracking() { fn remove_tracking() {
let mut world = World::new(); let mut world = World::new();
@ -352,7 +398,7 @@ mod tests {
*ran = true; *ran = true;
} }
run_system(&mut world, validate_removed.system()); run_system(&mut world, validate_removed);
assert!(*world.get_resource::<bool>().unwrap(), "system ran"); assert!(*world.get_resource::<bool>().unwrap(), "system ran");
} }
@ -365,18 +411,10 @@ mod tests {
*modified = true; *modified = true;
} }
run_system( run_system(&mut world, sys.config(|config| config.0 = Some(42)));
&mut world,
sys.system().config(|config| config.0 = Some(42)),
);
// ensure the system actually ran // ensure the system actually ran
assert!(*world.get_resource::<bool>().unwrap()); assert!(*world.get_resource::<bool>().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::<bool>().unwrap());
} }
#[test] #[test]
@ -417,7 +455,7 @@ mod tests {
*modified = true; *modified = true;
} }
run_system(&mut world, sys.system()); run_system(&mut world, sys);
// ensure the system actually ran // ensure the system actually ran
assert!(*world.get_resource::<bool>().unwrap()); assert!(*world.get_resource::<bool>().unwrap());
@ -528,12 +566,15 @@ mod tests {
world.insert_resource(A(42)); world.insert_resource(A(42));
world.spawn().insert(B(7)); world.spawn().insert(B(7));
let mut system_state: SystemState<(Res<A>, Query<&B>, QuerySet<(Query<&C>, Query<&D>)>)> = let mut system_state: SystemState<(
SystemState::new(&mut world); Res<A>,
Query<&B>,
QuerySet<(QueryState<&C>, QueryState<&D>)>,
)> = SystemState::new(&mut world);
let (a, query, _) = system_state.get(&world); let (a, query, _) = system_state.get(&world);
assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!( assert_eq!(
*query.single().unwrap(), *query.single(),
B(7), B(7),
"returned component matches initial value" "returned component matches initial value"
); );
@ -560,7 +601,7 @@ mod tests {
let (a, mut query) = system_state.get_mut(&mut world); let (a, mut query) = system_state.get_mut(&mut world);
assert_eq!(*a, A(42), "returned resource matches initial value"); assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!( assert_eq!(
*query.single_mut().unwrap(), *query.single_mut(),
B(7), B(7),
"returned component matches initial value" "returned component matches initial value"
); );
@ -577,18 +618,18 @@ mod tests {
let mut system_state: SystemState<Query<&A, Changed<A>>> = SystemState::new(&mut world); let mut system_state: SystemState<Query<&A, Changed<A>>> = SystemState::new(&mut world);
{ {
let query = system_state.get(&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); let query = system_state.get(&world);
assert!(query.single().is_err()); assert!(query.get_single().is_err());
} }
world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2; world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
{ {
let query = system_state.get(&world); 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<Res<'static, A>>,
state_q: SystemState<Query<'static, 'static, &'static A>>,
}
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<Holder<'w>> {
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<Entity>) {
/// 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<Entity>) {
/// 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<Entity>) {
/// 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<Entity>) {
/// 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<Entity>) {
/// 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<Entity>) {
/// 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<Query<'static, 'static, &'static A>>,
/// state_w: SystemState<Query<'static, 'static, &'static mut A>>,
/// }
///
/// 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<Query<'static, 'static, &'static A>>,
/// state_w: SystemState<Query<'static, 'static, &'static mut A>>,
/// }
///
/// 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() {}

View file

@ -1,36 +0,0 @@
use crate::system::{SystemParam, SystemParamFetch, SystemParamState, SystemState};
use crate::world::World;
pub struct ParamState<Param: SystemParam> {
state: SystemState,
param_state: <Param as SystemParam>::Fetch,
change_tick: u32,
}
impl<Param: SystemParam> ParamState<Param> {
pub fn new(world: &mut World) -> Self {
let mut state = SystemState::new::<Param>();
let config = <Param::Fetch as SystemParamState>::default_config();
let param_state = <Param::Fetch as SystemParamState>::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) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
let change_tick = world.increment_change_tick();
self.change_tick = change_tick;
// TODO: add/implement ReadOnlySystemParam and constrain param here
unsafe {
<Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,
&mut self.state,
world,
change_tick,
)
}
}
}

View file

@ -107,12 +107,12 @@ use thiserror::Error;
/// ///
/// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery) /// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery)
/// bevy has to offer. /// bevy has to offer.
pub struct Query<'w, 's, Q: WorldQuery, F: WorldQuery = ()> pub struct Query<'world, 'state, Q: WorldQuery, F: WorldQuery = ()>
where where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
{ {
pub(crate) world: &'w World, pub(crate) world: &'world World,
pub(crate) state: &'s QueryState<Q, F>, pub(crate) state: &'state QueryState<Q, F>,
pub(crate) last_change_tick: u32, pub(crate) last_change_tick: u32,
pub(crate) 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. /// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
#[inline] #[inline]
pub fn iter(&self) -> QueryIter<'w, 's, Q, F> pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F>
where where
Q::Fetch: ReadOnlyFetch, 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. /// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
/// This can only be called for read-only queries /// 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: all possible K-sized combinations of query results, without repetition
/// - if K > N: empty set (no K-sized combinations exist) /// - if K > N: empty set (no K-sized combinations exist)
#[inline] #[inline]
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'w, 's, Q, F, K> pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K>
where where
Q::Fetch: ReadOnlyFetch, 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. /// 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. /// The returned value is not an `Iterator`, because that would lead to aliasing of mutable references.
@ -216,7 +216,7 @@ where
#[inline] #[inline]
pub fn iter_combinations_mut<const K: usize>( pub fn iter_combinations_mut<const K: usize>(
&mut self, &mut self,
) -> QueryCombinationIter<'w, 's, Q, F, K> { ) -> QueryCombinationIter<'_, '_, Q, F, K> {
// SAFE: system runs without conflicts with other systems. // SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
unsafe { unsafe {
@ -235,7 +235,7 @@ where
/// This function makes it possible to violate Rust's aliasing guarantees. You must make sure /// 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 /// this call does not result in multiple mutable references to the same component
#[inline] #[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. // SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
self.state self.state
@ -251,7 +251,7 @@ where
#[inline] #[inline]
pub unsafe fn iter_combinations_unsafe<const K: usize>( pub unsafe fn iter_combinations_unsafe<const K: usize>(
&self, &self,
) -> QueryCombinationIter<'w, 's, Q, F, K> { ) -> QueryCombinationIter<'_, '_, Q, F, K> {
// SEMI-SAFE: system runs without conflicts with other systems. // SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
self.state.iter_combinations_unchecked_manual( 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. /// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
#[inline] #[inline]
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item)) pub fn for_each(&'s self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
where where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
@ -285,7 +285,7 @@ where
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot /// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// be chained like a normal [`Iterator`]. /// be chained like a normal [`Iterator`].
#[inline] #[inline]
pub fn for_each_mut(&mut self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item)) { pub fn for_each_mut<'a>(&'a mut self, f: impl FnMut(<Q::Fetch as Fetch<'a, 'a>>::Item)) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict // borrow checks when they conflict
unsafe { unsafe {
@ -304,7 +304,7 @@ where
/// write-queries. /// write-queries.
#[inline] #[inline]
pub fn par_for_each( pub fn par_for_each(
&self, &'s self,
task_pool: &TaskPool, task_pool: &TaskPool,
batch_size: usize, batch_size: usize,
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone, f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone,
@ -327,11 +327,11 @@ where
/// Runs `f` on each query result in parallel using the given task pool. /// Runs `f` on each query result in parallel using the given task pool.
#[inline] #[inline]
pub fn par_for_each_mut( pub fn par_for_each_mut<'a>(
&mut self, &'a mut self,
task_pool: &TaskPool, task_pool: &TaskPool,
batch_size: usize, batch_size: usize,
f: impl Fn(<Q::Fetch as Fetch<'w, 's>>::Item) + Send + Sync + Clone, f: impl Fn(<Q::Fetch as Fetch<'a, 'a>>::Item) + Send + Sync + Clone,
) { ) {
// SAFE: system runs without conflicts with other systems. same-system queries have runtime // SAFE: system runs without conflicts with other systems. same-system queries have runtime
// borrow checks when they conflict // 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. /// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
#[inline] #[inline]
pub fn get(&self, entity: Entity) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> pub fn get(
&'s self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where where
Q::Fetch: ReadOnlyFetch, Q::Fetch: ReadOnlyFetch,
{ {
@ -372,7 +375,7 @@ where
pub fn get_mut( pub fn get_mut(
&mut self, &mut self,
entity: Entity, entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> { ) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// SAFE: system runs without conflicts with other systems. // SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict // same-system queries have runtime borrow checks when they conflict
unsafe { unsafe {
@ -393,7 +396,7 @@ where
/// this call does not result in multiple mutable references to the same component /// this call does not result in multiple mutable references to the same component
#[inline] #[inline]
pub unsafe fn get_unchecked( pub unsafe fn get_unchecked(
&'s self, &self,
entity: Entity, entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> { ) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// SEMI-SAFE: system runs without conflicts with other systems. // SEMI-SAFE: system runs without conflicts with other systems.
@ -442,7 +445,7 @@ where
pub fn get_component_mut<T: Component>( pub fn get_component_mut<T: Component>(
&mut self, &mut self,
entity: Entity, entity: Entity,
) -> Result<Mut<'w, T>, QueryComponentError> { ) -> Result<Mut<'_, T>, QueryComponentError> {
// SAFE: unique access to query (preventing aliased access) // SAFE: unique access to query (preventing aliased access)
unsafe { self.get_component_unchecked_mut(entity) } unsafe { self.get_component_unchecked_mut(entity) }
} }
@ -459,7 +462,7 @@ where
pub unsafe fn get_component_unchecked_mut<T: Component>( pub unsafe fn get_component_unchecked_mut<T: Component>(
&self, &self,
entity: Entity, entity: Entity,
) -> Result<Mut<'w, T>, QueryComponentError> { ) -> Result<Mut<'_, T>, QueryComponentError> {
let world = self.world; let world = self.world;
let entity_ref = world let entity_ref = world
.get_entity(entity) .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<Player>>) {
/// 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) -> <Q::Fetch as Fetch<'w, 's>>::Item
where
Q::Fetch: ReadOnlyFetch,
{
self.get_single().unwrap()
}
/// Gets the result of a single-result query. /// Gets the result of a single-result query.
/// ///
/// If the query has exactly one result, returns the result inside `Ok` /// If the query has exactly one result, returns the result inside `Ok`
@ -494,27 +524,28 @@ where
/// # Examples /// # Examples
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::{IntoSystem, With};
/// # use bevy_ecs::system::{Query, QuerySingleError}; /// # use bevy_ecs::system::{Query, QuerySingleError};
/// # use bevy_ecs::prelude::IntoSystem; /// struct Player;
/// struct PlayerScore(i32); /// struct Position(f32, f32);
/// fn player_scoring_system(query: Query<&PlayerScore>) { /// fn player_system(query: Query<&Position, With<Player>>) {
/// match query.single() { /// match query.get_single() {
/// Ok(PlayerScore(score)) => { /// Ok(position) => {
/// // do something with score /// // do something with position
/// } /// }
/// Err(QuerySingleError::NoEntities(_)) => { /// Err(QuerySingleError::NoEntities(_)) => {
/// // no PlayerScore /// // no position with Player
/// } /// }
/// Err(QuerySingleError::MultipleEntities(_)) => { /// 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. /// This can only be called for read-only queries, see [`Self::get_single_mut`] for write-queries.
pub fn single(&self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError> pub fn get_single(&'s self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
where where
Q::Fetch: ReadOnlyFetch, 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) -> <Q::Fetch as Fetch<'_, '_>>::Item {
self.get_single_mut().unwrap()
}
/// Gets the query result if it is only a single result, otherwise returns a /// Gets the query result if it is only a single result, otherwise returns a
/// [`QuerySingleError`]. /// [`QuerySingleError`].
pub fn single_mut(&mut self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError> { pub fn get_single_mut(
&mut self,
) -> Result<<Q::Fetch as Fetch<'_, '_>>::Item, QuerySingleError> {
let mut query = self.iter_mut(); let mut query = self.iter_mut();
let first = query.next(); let first = query.next();
let extra = query.next().is_some(); let extra = query.next().is_some();

View file

@ -24,7 +24,7 @@ impl SystemId {
/// ///
/// Systems are functions with all arguments implementing [SystemParam](crate::system::SystemParam). /// 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. /// 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. /// Systems are executed in parallel, in opportunistic order; data access is managed automatically.

View file

@ -27,13 +27,12 @@ use std::{
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # use std::marker::PhantomData; /// use std::marker::PhantomData;
/// use bevy_ecs::system::SystemParam; /// use bevy_ecs::system::SystemParam;
/// ///
/// #[derive(SystemParam)] /// #[derive(SystemParam)]
/// struct MyParam<'s, 'w> { /// struct MyParam<'w, 's> {
/// foo: Res<'w, usize>, /// foo: Res<'w, usize>,
/// // TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
/// #[system_param(ignore)] /// #[system_param(ignore)]
/// marker: PhantomData<&'s usize>, /// marker: PhantomData<&'s usize>,
/// } /// }
@ -45,7 +44,7 @@ use std::{
/// # my_system.system(); /// # my_system.system();
/// ``` /// ```
pub trait SystemParam: Sized { 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`]. /// 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`] /// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`]
pub unsafe trait ReadOnlySystemParamFetch {} pub unsafe trait ReadOnlySystemParamFetch {}
pub trait SystemParamFetch<'s, 'w>: SystemParamState { pub trait SystemParamFetch<'world, 'state>: SystemParamState {
type Item; type Item;
/// # Safety /// # Safety
/// ///
/// This call might access any of the input parameters in an unsafe way. Make sure the data /// 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. /// access is safe in the context of the system scheduler.
unsafe fn get_param( unsafe fn get_param(
state: &'s mut Self, state: &'state mut Self,
system_meta: &SystemMeta, system_meta: &SystemMeta,
world: &'w World, world: &'world World,
change_tick: u32, change_tick: u32,
) -> Self::Item; ) -> 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 where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
{ {
@ -150,7 +149,7 @@ where
fn default_config() {} 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<Q, F> for QueryState<Q, F>
where where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
@ -189,7 +188,13 @@ fn assert_component_access_compatibility(
query_type, filter_type, system_name, accesses); query_type, filter_type, system_name, accesses);
} }
pub struct QuerySet<T>(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>(T); pub struct QuerySetState<T>(T);
impl_query_set!(); impl_query_set!();
@ -254,7 +259,7 @@ impl<'w, T: Component> AsRef<T> for Res<'w, T> {
} }
} }
/// The [`SystemParamState`] of [`Res`]. /// The [`SystemParamState`] of [`Res<T>`].
pub struct ResState<T> { pub struct ResState<T> {
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<T>, marker: PhantomData<T>,
@ -295,7 +300,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState<T> { impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
type Item = Res<'w, T>; type Item = Res<'w, T>;
#[inline] #[inline]
@ -323,7 +328,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResState<T> {
} }
} }
/// The [`SystemParamState`] of `Option<Res<T>>`. /// The [`SystemParamState`] of [`Option<Res<T>>`].
/// See: [`Res<T>`]
pub struct OptionResState<T>(ResState<T>); pub struct OptionResState<T>(ResState<T>);
impl<'a, T: Component> SystemParam for Option<Res<'a, T>> { impl<'a, T: Component> SystemParam for Option<Res<'a, T>> {
@ -343,7 +349,7 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState<T> { impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState<T> {
type Item = Option<Res<'w, T>>; type Item = Option<Res<'w, T>>;
#[inline] #[inline]
@ -364,7 +370,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResState<T> {
} }
} }
/// The [`SystemParamState`] of [`ResMut`]. /// The [`SystemParamState`] of [`ResMut<T>`].
pub struct ResMutState<T> { pub struct ResMutState<T> {
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<T>, marker: PhantomData<T>,
@ -409,7 +415,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState<T> { impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
type Item = ResMut<'w, T>; type Item = ResMut<'w, T>;
#[inline] #[inline]
@ -439,7 +445,8 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for ResMutState<T> {
} }
} }
/// The [`SystemParamState`] of `Option<ResMut<T>>`. /// The [`SystemParamState`] of [`Option<ResMut<T>>`].
/// See: [`ResMut<T>`]
pub struct OptionResMutState<T>(ResMutState<T>); pub struct OptionResMutState<T>(ResMutState<T>);
impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> { impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> {
@ -456,7 +463,7 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState<T> { impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState<T> {
type Item = Option<ResMut<'w, T>>; type Item = Option<ResMut<'w, T>>;
#[inline] #[inline]
@ -479,7 +486,7 @@ impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for OptionResMutState<T> {
} }
} }
impl<'s, 'w> SystemParam for Commands<'s, 'w> { impl<'w, 's> SystemParam for Commands<'w, 's> {
type Fetch = CommandQueue; type Fetch = CommandQueue;
} }
@ -501,8 +508,8 @@ unsafe impl SystemParamState for CommandQueue {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for CommandQueue { impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
type Item = Commands<'s, 'w>; type Item = Commands<'w, 's>;
#[inline] #[inline]
unsafe fn get_param( 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<T>`].
pub struct LocalState<T: Component>(T); pub struct LocalState<T: Component>(T);
impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> { impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> {
@ -591,7 +598,7 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
} }
} }
impl<'s, 'w, T: Component + FromWorld> SystemParamFetch<'s, 'w> for LocalState<T> { impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
type Item = Local<'s, T>; type Item = Local<'s, T>;
#[inline] #[inline]
@ -639,7 +646,7 @@ impl<'a, T> RemovedComponents<'a, T> {
// SAFE: Only reads World components // SAFE: Only reads World components
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {} unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
/// The [`SystemParamState`] of [`RemovedComponents`]. /// The [`SystemParamState`] of [`RemovedComponents<T>`].
pub struct RemovedComponentsState<T> { pub struct RemovedComponentsState<T> {
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<T>, marker: PhantomData<T>,
@ -664,7 +671,7 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: Component> SystemParamFetch<'s, 'w> for RemovedComponentsState<T> { impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState<T> {
type Item = RemovedComponents<'w, T>; type Item = RemovedComponents<'w, T>;
#[inline] #[inline]
@ -736,7 +743,7 @@ impl<'w, T: 'static> Deref for NonSend<'w, T> {
} }
} }
/// The [`SystemParamState`] of [`NonSend`]. /// The [`SystemParamState`] of [`NonSend<T>`].
pub struct NonSendState<T> { pub struct NonSendState<T> {
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<fn() -> T>, marker: PhantomData<fn() -> T>,
@ -779,7 +786,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState<T> { impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState<T> {
type Item = NonSend<'w, T>; type Item = NonSend<'w, T>;
#[inline] #[inline]
@ -809,10 +816,11 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendState<T> {
} }
} }
/// The [`SystemParamState`] of `Option<NonSend<T>>`. /// The [`SystemParamState`] of [`Option<NonSend<T>>`].
/// See: [`NonSend<T>`]
pub struct OptionNonSendState<T>(NonSendState<T>); pub struct OptionNonSendState<T>(NonSendState<T>);
impl<'a, T: Component> SystemParam for Option<NonSend<'a, T>> { impl<'w, T: 'static> SystemParam for Option<NonSend<'w, T>> {
type Fetch = OptionNonSendState<T>; type Fetch = OptionNonSendState<T>;
} }
@ -829,7 +837,7 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState<T> { impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState<T> {
type Item = Option<NonSend<'w, T>>; type Item = Option<NonSend<'w, T>>;
#[inline] #[inline]
@ -851,7 +859,7 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendState<T> {
} }
} }
/// The [`SystemParamState`] of [`NonSendMut`]. /// The [`SystemParamState`] of [`NonSendMut<T>`].
pub struct NonSendMutState<T> { pub struct NonSendMutState<T> {
component_id: ComponentId, component_id: ComponentId,
marker: PhantomData<fn() -> T>, marker: PhantomData<fn() -> T>,
@ -869,7 +877,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
system_meta.set_non_send(); system_meta.set_non_send();
let component_id = world.components.get_or_insert_non_send_resource_id::<T>(); let component_id = world.initialize_non_send_resource::<T>();
let combined_access = system_meta.component_access_set.combined_access_mut(); let combined_access = system_meta.component_access_set.combined_access_mut();
if combined_access.has_write(component_id) { if combined_access.has_write(component_id) {
panic!( panic!(
@ -898,7 +906,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState<T> { impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState<T> {
type Item = NonSendMut<'w, T>; type Item = NonSendMut<'w, T>;
#[inline] #[inline]
@ -929,7 +937,8 @@ impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for NonSendMutState<T> {
} }
} }
/// The [`SystemParamState`] of `Option<NonSendMut<T>>`. /// The [`SystemParamState`] of [`Option<NonSendMut<T>>`].
/// See: [`NonSendMut<T>`]
pub struct OptionNonSendMutState<T>(NonSendMutState<T>); pub struct OptionNonSendMutState<T>(NonSendMutState<T>);
impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> { impl<'a, T: 'static> SystemParam for Option<NonSendMut<'a, T>> {
@ -946,7 +955,7 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendMutState<T> {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w, T: 'static> SystemParamFetch<'s, 'w> for OptionNonSendMutState<T> { impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState<T> {
type Item = Option<NonSendMut<'w, T>>; type Item = Option<NonSendMut<'w, T>>;
#[inline] #[inline]
@ -991,7 +1000,7 @@ unsafe impl SystemParamState for ArchetypesState {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for ArchetypesState { impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState {
type Item = &'w Archetypes; type Item = &'w Archetypes;
#[inline] #[inline]
@ -1026,7 +1035,7 @@ unsafe impl SystemParamState for ComponentsState {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for ComponentsState { impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState {
type Item = &'w Components; type Item = &'w Components;
#[inline] #[inline]
@ -1061,7 +1070,7 @@ unsafe impl SystemParamState for EntitiesState {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for EntitiesState { impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState {
type Item = &'w Entities; type Item = &'w Entities;
#[inline] #[inline]
@ -1096,7 +1105,7 @@ unsafe impl SystemParamState for BundlesState {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for BundlesState { impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState {
type Item = &'w Bundles; type Item = &'w Bundles;
#[inline] #[inline]
@ -1123,7 +1132,7 @@ impl SystemParam for SystemChangeTick {
type Fetch = SystemChangeTickState; type Fetch = SystemChangeTickState;
} }
/// The [`SystemParamState`] of [`SystemChangeTickState`]. /// The [`SystemParamState`] of [`SystemChangeTick`].
pub struct SystemChangeTickState {} pub struct SystemChangeTickState {}
unsafe impl SystemParamState for SystemChangeTickState { unsafe impl SystemParamState for SystemChangeTickState {
@ -1136,7 +1145,7 @@ unsafe impl SystemParamState for SystemChangeTickState {
fn default_config() {} fn default_config() {}
} }
impl<'s, 'w> SystemParamFetch<'s, 'w> for SystemChangeTickState { impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState {
type Item = SystemChangeTick; type Item = SystemChangeTick;
unsafe fn get_param( unsafe fn get_param(
@ -1163,7 +1172,7 @@ macro_rules! impl_system_param_tuple {
#[allow(unused_variables)] #[allow(unused_variables)]
#[allow(non_snake_case)] #[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,)*); type Item = ($($param::Item,)*);
#[inline] #[inline]

View file

@ -1,5 +1,5 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus}, archetype::{Archetype, ArchetypeId, Archetypes},
bundle::{Bundle, BundleInfo}, bundle::{Bundle, BundleInfo},
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType}, 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<ComponentStatus>, 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<T: Bundle>(&mut self, bundle: T) -> &mut Self { pub fn insert_bundle<T: Bundle>(&mut self, bundle: T) -> &mut Self {
let change_tick = self.world.change_tick(); let change_tick = self.world.change_tick();
let bundle_info = self let bundle_info = self
.world .world
.bundles .bundles
.init_info::<T>(&mut self.world.components); .init_info::<T>(&mut self.world.components);
let mut bundle_inserter = bundle_info.get_bundle_inserter(
let (archetype, bundle_status, new_location) = unsafe { &mut self.world.entities,
Self::get_insert_bundle_info( &mut self.world.archetypes,
&mut self.world.entities, &mut self.world.components,
&mut self.world.archetypes, &mut self.world.storages,
&mut self.world.components, self.location.archetype_id,
&mut self.world.storages, change_tick,
bundle_info, );
self.location, // SAFE: location matches current entity. `T` matches `bundle_info`
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
unsafe { unsafe {
bundle_info.write_components( self.location = bundle_inserter.insert(self.entity, self.location.index, bundle);
&mut self.world.storages.sparse_sets, }
self.entity,
table,
table_row,
bundle_status,
bundle,
change_tick,
)
};
self self
} }
// TODO: move to BundleInfo
pub fn remove_bundle<T: Bundle>(&mut self) -> Option<T> { pub fn remove_bundle<T: Bundle>(&mut self) -> Option<T> {
let archetypes = &mut self.world.archetypes; let archetypes = &mut self.world.archetypes;
let storages = &mut self.world.storages; let storages = &mut self.world.storages;
@ -340,6 +257,42 @@ impl<'w> EntityMut<'w> {
}) })
}; };
unsafe {
Self::move_entity_from_remove::<false>(
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<const DROP: bool>(
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); let remove_result = old_archetype.swap_remove(old_location.index);
if let Some(swapped_entity) = remove_result.swapped_entity { if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location; 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_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_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 { } else {
let (old_table, new_table) = storages let (old_table, new_table) = storages
.tables .tables
.get_2_mut(old_table_id, new_archetype.table_id()); .get_2_mut(old_table_id, new_archetype.table_id());
// SAFE: table_row exists. All "missing" components have been extracted into the bundle // SAFE: old_table_row exists
// above and the caller takes ownership let move_result = if DROP {
let move_result = old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
unsafe { old_table.move_to_and_forget_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 // SAFE: move_result.new_row is a valid position in new_archetype's table
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) }; 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 an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity { if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap(); let swapped_location = entities.get(swapped_entity).unwrap();
let archetype = &mut archetypes[swapped_location.archetype_id]; archetypes[swapped_location.archetype_id]
archetype.set_entity_table_row(swapped_location.index, old_table_row); .set_entity_table_row(swapped_location.index, old_table_row);
} }
new_location new_location
}; };
self.location = new_location; *self_location = new_location;
entities.meta[self.entity.id as usize].location = new_location; entities.meta[entity.id as usize].location = new_location;
Some(result)
} }
// TODO: move to BundleInfo
/// Remove any components in the bundle that the entity has. /// Remove any components in the bundle that the entity has.
pub fn remove_bundle_intersection<T: Bundle>(&mut self) { pub fn remove_bundle_intersection<T: Bundle>(&mut self) {
let archetypes = &mut self.world.archetypes; let archetypes = &mut self.world.archetypes;
@ -425,40 +379,18 @@ impl<'w> EntityMut<'w> {
} }
} }
let remove_result = old_archetype.swap_remove(old_location.index); unsafe {
if let Some(swapped_entity) = remove_result.swapped_entity { Self::move_entity_from_remove::<true>(
entities.meta[swapped_entity.id as usize].location = old_location; 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<T: Component>(&mut self, value: T) -> &mut Self { pub fn insert<T: Component>(&mut self, value: T) -> &mut Self {
@ -531,6 +463,7 @@ impl<'w> EntityMut<'w> {
} }
} }
// TODO: move to Storages?
/// # Safety /// # Safety
/// `entity_location` must be within bounds of the given archetype and `entity` must exist inside /// `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype /// the archetype
@ -560,6 +493,7 @@ unsafe fn get_component(
} }
} }
// TODO: move to Storages?
/// # Safety /// # Safety
/// Caller must ensure that `component_id` is valid /// Caller must ensure that `component_id` is valid
#[inline] #[inline]
@ -590,6 +524,7 @@ unsafe fn get_component_and_ticks(
} }
} }
// TODO: move to Storages?
/// Moves component data out of storage. /// Moves component data out of storage.
/// ///
/// This function leaves the underlying memory unchanged, but the component behind /// 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) 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 /// 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 /// 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. /// change. Results are cached in the Archetype Graph to avoid redundant work.
@ -814,7 +660,7 @@ unsafe fn remove_bundle_from_archetype(
// graph // graph
current_archetype current_archetype
.edges_mut() .edges_mut()
.set_remove_bundle(bundle_info.id, None); .insert_remove_bundle(bundle_info.id, None);
return None; return None;
} }
} }
@ -853,11 +699,11 @@ unsafe fn remove_bundle_from_archetype(
if intersection { if intersection {
current_archetype current_archetype
.edges_mut() .edges_mut()
.set_remove_bundle_intersection(bundle_info.id, result); .insert_remove_bundle_intersection(bundle_info.id, result);
} else { } else {
current_archetype current_archetype
.edges_mut() .edges_mut()
.set_remove_bundle(bundle_info.id, result); .insert_remove_bundle(bundle_info.id, result);
} }
result result
} }

View file

@ -1,16 +1,15 @@
mod entity_ref; mod entity_ref;
mod pointer;
mod spawn_batch; mod spawn_batch;
mod world_cell; mod world_cell;
pub use crate::change_detection::Mut;
pub use entity_ref::*; pub use entity_ref::*;
pub use pointer::*;
pub use spawn_batch::*; pub use spawn_batch::*;
pub use world_cell::*; pub use world_cell::*;
use crate::{ use crate::{
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes}, archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
bundle::{Bundle, Bundles}, bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::Ticks, change_detection::Ticks,
component::{ component::{
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError, Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
@ -221,8 +220,13 @@ impl World {
self.get_entity_mut(entity).expect("Entity does not exist") 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. /// 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. /// 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] #[inline]
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityMut> { pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityMut> {
self.flush(); self.flush();
@ -379,6 +383,7 @@ impl World {
/// .id(); /// .id();
/// let position = world.get::<Position>(entity).unwrap(); /// let position = world.get::<Position>(entity).unwrap();
/// assert_eq!(position.x, 0.0); /// assert_eq!(position.x, 0.0);
/// ```
#[inline] #[inline]
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> { pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
self.get_entity(entity)?.get() self.get_entity(entity)?.get()
@ -400,6 +405,7 @@ impl World {
/// .id(); /// .id();
/// let mut position = world.get_mut::<Position>(entity).unwrap(); /// let mut position = world.get_mut::<Position>(entity).unwrap();
/// position.x = 1.0; /// position.x = 1.0;
/// ```
#[inline] #[inline]
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> { pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
self.get_entity_mut(entity)?.get_mut() self.get_entity_mut(entity)?.get_mut()
@ -699,6 +705,127 @@ impl World {
self.get_non_send_unchecked_mut_with_id(component_id) 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::<f64>(e0), Some(&0.0));
/// ```
pub fn insert_or_spawn_batch<I, B>(&mut self, iter: I) -> Result<(), Vec<Entity>>
where
I: IntoIterator,
I::IntoIter: Iterator<Item = (Entity, B)>,
B: Bundle,
{
self.flush();
let iter = iter.into_iter();
let change_tick = *self.change_tick.get_mut();
let bundle_info = self.bundles.init_info::<B>(&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 /// 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 /// returning. This enables safe mutable access to a resource while still providing mutable
/// world access /// world access
@ -838,7 +965,7 @@ impl World {
let resource_archetype = self let resource_archetype = self
.archetypes .archetypes
.archetypes .archetypes
.get_unchecked_mut(ArchetypeId::resource().index()); .get_unchecked_mut(ArchetypeId::RESOURCE.index());
let resource_archetype_components = &mut resource_archetype.components; let resource_archetype_components = &mut resource_archetype.components;
let archetype_component_count = &mut self.archetypes.archetype_component_count; let archetype_component_count = &mut self.archetypes.archetype_component_count;
let components = &self.components; let components = &self.components;

View file

@ -1 +0,0 @@
pub use crate::change_detection::Mut;

View file

@ -1,9 +1,7 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeId, ComponentStatus}, bundle::{Bundle, BundleSpawner},
bundle::{Bundle, BundleInfo}, entity::Entity,
entity::{Entities, Entity}, world::World,
storage::{SparseSets, Table},
world::{add_bundle_to_archetype, World},
}; };
pub struct SpawnBatchIter<'w, I> pub struct SpawnBatchIter<'w, I>
@ -12,13 +10,7 @@ where
I::Item: Bundle, I::Item: Bundle,
{ {
inner: I, inner: I,
entities: &'w mut Entities, spawner: BundleSpawner<'w, 'w>,
archetype: &'w mut Archetype,
table: &'w mut Table,
sparse_sets: &'w mut SparseSets,
bundle_info: &'w BundleInfo,
bundle_status: &'w [ComponentStatus],
change_tick: u32,
} }
impl<'w, I> SpawnBatchIter<'w, I> impl<'w, I> SpawnBatchIter<'w, I>
@ -33,40 +25,22 @@ where
world.flush(); world.flush();
let (lower, upper) = iter.size_hint(); let (lower, upper) = iter.size_hint();
let length = upper.unwrap_or(lower);
let bundle_info = world.bundles.init_info::<I::Item>(&mut world.components); let bundle_info = world.bundles.init_info::<I::Item>(&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); world.entities.reserve(length as u32);
let edge = empty_archetype let mut spawner = bundle_info.get_bundle_spawner(
.edges() &mut world.entities,
.get_add_bundle(bundle_info.id()) &mut world.archetypes,
.unwrap(); &mut world.components,
&mut world.storages,
*world.change_tick.get_mut(),
);
spawner.reserve_storage(length);
Self { Self {
inner: iter, inner: iter,
entities: &mut world.entities, spawner,
archetype,
table,
sparse_sets: &mut world.storages.sparse_sets,
bundle_info,
change_tick: *world.change_tick.get_mut(),
bundle_status: &edge.bundle_status,
} }
} }
} }
@ -90,24 +64,8 @@ where
fn next(&mut self) -> Option<Entity> { fn next(&mut self) -> Option<Entity> {
let bundle = self.inner.next()?; let bundle = self.inner.next()?;
let entity = self.entities.alloc(); // SAFE: bundle matches spawner type
// SAFE: component values are immediately written to relevant storages (which have been unsafe { Some(self.spawner.spawn(bundle)) }
// 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)
} }
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {

View file

@ -296,7 +296,7 @@ mod tests {
.components .components
.get_resource_id(TypeId::of::<u32>()) .get_resource_id(TypeId::of::<u32>())
.unwrap(); .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 let u32_archetype_component_id = resource_archetype
.get_archetype_component_id(u32_component_id) .get_archetype_component_id(u32_component_id)
.unwrap(); .unwrap();

View file

@ -2,10 +2,6 @@
name = "bevy_render" name = "bevy_render"
version = "0.5.0" version = "0.5.0"
edition = "2018" edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "Provides rendering functionality for Bevy Engine" description = "Provides rendering functionality for Bevy Engine"
homepage = "https://bevyengine.org" homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy" repository = "https://github.com/bevyengine/bevy"

View file

@ -4,9 +4,10 @@ use bevy_ecs::{
component::Component, component::Component,
entity::Entity, entity::Entity,
event::EventReader, event::EventReader,
prelude::QueryState,
query::Added, query::Added,
reflect::ReflectComponent, reflect::ReflectComponent,
system::{Query, QuerySet, Res}, system::{QuerySet, Res},
}; };
use bevy_math::{Mat4, Vec2, Vec3}; use bevy_math::{Mat4, Vec2, Vec3};
use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_reflect::{Reflect, ReflectDeserialize};
@ -70,8 +71,8 @@ pub fn camera_system<T: CameraProjection + Component>(
mut window_created_events: EventReader<WindowCreated>, mut window_created_events: EventReader<WindowCreated>,
windows: Res<Windows>, windows: Res<Windows>,
mut queries: QuerySet<( mut queries: QuerySet<(
Query<(Entity, &mut Camera, &mut T)>, QueryState<(Entity, &mut Camera, &mut T)>,
Query<Entity, Added<Camera>>, QueryState<Entity, Added<Camera>>,
)>, )>,
) { ) {
let mut changed_window_ids = Vec::new(); let mut changed_window_ids = Vec::new();
@ -99,7 +100,7 @@ pub fn camera_system<T: CameraProjection + Component>(
for entity in &mut queries.q1().iter() { for entity in &mut queries.q1().iter() {
added_cameras.push(entity); 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 let Some(window) = windows.get(camera.window) {
if changed_window_ids.contains(&window.id()) if changed_window_ids.contains(&window.id())
|| added_cameras.contains(&entity) || added_cameras.contains(&entity)

View file

@ -10,6 +10,7 @@ use bevy_math::{Vec3, Vec4};
use bevy_reflect::{Reflect, ReflectDeserialize}; use bevy_reflect::{Reflect, ReflectDeserialize};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Mul, MulAssign}; use std::ops::{Add, AddAssign, Mul, MulAssign};
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Reflect)]
#[reflect(PartialEq, Serialize, Deserialize)] #[reflect(PartialEq, Serialize, Deserialize)]
@ -1070,10 +1071,12 @@ impl Bytes for Color {
impl_render_resource_bytes!(Color); impl_render_resource_bytes!(Color);
#[derive(Debug)] #[derive(Debug, Error)]
pub enum HexColorError { pub enum HexColorError {
#[error("Unexpected length of hex string")]
Length, Length,
Hex(hex::FromHexError), #[error("Error parsing hex value")]
Hex(#[from] hex::FromHexError),
} }
fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> { fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> {

View file

@ -164,7 +164,7 @@ pub enum DrawError {
} }
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct DrawContext<'s, 'w> { pub struct DrawContext<'w, 's> {
pub pipelines: ResMut<'w, Assets<PipelineDescriptor>>, pub pipelines: ResMut<'w, Assets<PipelineDescriptor>>,
pub shaders: ResMut<'w, Assets<Shader>>, pub shaders: ResMut<'w, Assets<Shader>>,
pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>, pub asset_render_resource_bindings: ResMut<'w, AssetRenderResourceBindings>,
@ -177,7 +177,7 @@ pub struct DrawContext<'s, 'w> {
marker: PhantomData<&'s usize>, marker: PhantomData<&'s usize>,
} }
impl<'s, 'w> DrawContext<'s, 'w> { impl<'w, 's> DrawContext<'w, 's> {
pub fn get_uniform_buffer<T: RenderResource>( pub fn get_uniform_buffer<T: RenderResource>(
&mut self, &mut self,
render_resource: &T, render_resource: &T,

View file

@ -14,7 +14,7 @@ pub mod wireframe;
use bevy_ecs::{ use bevy_ecs::{
schedule::{ParallelSystemDescriptorCoercion, SystemStage}, schedule::{ParallelSystemDescriptorCoercion, SystemStage},
system::{IntoExclusiveSystem, IntoSystem, Res}, system::{IntoExclusiveSystem, Res},
}; };
use bevy_transform::TransformSystem; use bevy_transform::TransformSystem;
use bevy_utils::tracing::warn; use bevy_utils::tracing::warn;
@ -174,58 +174,38 @@ impl Plugin for RenderPlugin {
.init_resource::<RenderResourceBindings>() .init_resource::<RenderResourceBindings>()
.init_resource::<AssetRenderResourceBindings>() .init_resource::<AssetRenderResourceBindings>()
.init_resource::<ActiveCameras>() .init_resource::<ActiveCameras>()
.add_startup_system_to_stage( .add_startup_system_to_stage(StartupStage::PreStartup, check_for_render_resource_context)
StartupStage::PreStartup, .add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system)
check_for_render_resource_context.system(), .add_system_to_stage(CoreStage::PostUpdate, camera::active_cameras_system)
)
.add_system_to_stage(CoreStage::PreUpdate, draw::clear_draw_system.system())
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::active_cameras_system.system(), camera::camera_system::<OrthographicProjection>.before(RenderSystem::VisibleEntities),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::camera_system::<OrthographicProjection> camera::camera_system::<PerspectiveProjection>.before(RenderSystem::VisibleEntities),
.system()
.before(RenderSystem::VisibleEntities),
)
.add_system_to_stage(
CoreStage::PostUpdate,
camera::camera_system::<PerspectiveProjection>
.system()
.before(RenderSystem::VisibleEntities),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
camera::visible_entities_system camera::visible_entities_system
.system()
.label(RenderSystem::VisibleEntities) .label(RenderSystem::VisibleEntities)
.after(TransformSystem::TransformPropagate), .after(TransformSystem::TransformPropagate),
) )
.add_system_to_stage(RenderStage::RenderResource, shader::shader_update_system)
.add_system_to_stage( .add_system_to_stage(
RenderStage::RenderResource, RenderStage::RenderResource,
shader::shader_update_system.system(), mesh::mesh_resource_provider_system,
) )
.add_system_to_stage( .add_system_to_stage(
RenderStage::RenderResource, RenderStage::RenderResource,
mesh::mesh_resource_provider_system.system(), Texture::texture_resource_system,
)
.add_system_to_stage(
RenderStage::RenderResource,
Texture::texture_resource_system.system(),
) )
.add_system_to_stage( .add_system_to_stage(
RenderStage::RenderGraphSystems, RenderStage::RenderGraphSystems,
render_graph::render_graph_schedule_executor_system.exclusive_system(), render_graph::render_graph_schedule_executor_system.exclusive_system(),
) )
.add_system_to_stage( .add_system_to_stage(RenderStage::Draw, pipeline::draw_render_pipelines_system)
RenderStage::Draw, .add_system_to_stage(RenderStage::PostRender, shader::clear_shader_defs_system);
pipeline::draw_render_pipelines_system.system(),
)
.add_system_to_stage(
RenderStage::PostRender,
shader::clear_shader_defs_system.system(),
);
if let Some(ref config) = self.base_render_graph_config { if let Some(ref config) = self.base_render_graph_config {
crate::base::add_base_graph(config, &mut app.world); crate::base::add_base_graph(config, &mut app.world);

View file

@ -9,8 +9,9 @@ use bevy_core::cast_slice;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
event::EventReader, event::EventReader,
prelude::QueryState,
query::{Changed, With}, query::{Changed, With},
system::{Local, Query, QuerySet, Res}, system::{Local, QuerySet, Res},
world::Mut, world::Mut,
}; };
use bevy_math::*; use bevy_math::*;
@ -512,8 +513,8 @@ pub fn mesh_resource_provider_system(
meshes: Res<Assets<Mesh>>, meshes: Res<Assets<Mesh>>,
mut mesh_events: EventReader<AssetEvent<Mesh>>, mut mesh_events: EventReader<AssetEvent<Mesh>>,
mut queries: QuerySet<( mut queries: QuerySet<(
Query<&mut RenderPipelines, With<Handle<Mesh>>>, QueryState<&mut RenderPipelines, With<Handle<Mesh>>>,
Query<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>, QueryState<(Entity, &Handle<Mesh>, &mut RenderPipelines), Changed<Handle<Mesh>>>,
)>, )>,
) { ) {
let mut changed_meshes = HashSet::default(); 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) { if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) {
for entity in mesh_entities.entities.iter() { 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( update_entity_mesh(
render_resource_context, render_resource_context,
mesh, mesh,
@ -587,7 +588,7 @@ pub fn mesh_resource_provider_system(
} }
// handover buffers to pipeline // 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 let mesh_entities = state
.mesh_entities .mesh_entities
.entry(handle.clone_weak()) .entry(handle.clone_weak())

View file

@ -8,7 +8,7 @@ use crate::{
}; };
use bevy_core::bytes_of; use bevy_core::bytes_of;
use bevy_ecs::{ use bevy_ecs::{
system::{BoxedSystem, IntoSystem, Local, Query, Res, ResMut}, system::{BoxedSystem, ConfigurableSystem, Local, Query, Res, ResMut},
world::World, world::World,
}; };
use bevy_transform::prelude::*; use bevy_transform::prelude::*;
@ -46,7 +46,7 @@ impl Node for CameraNode {
impl SystemNode for CameraNode { impl SystemNode for CameraNode {
fn get_system(&self) -> BoxedSystem { fn get_system(&self) -> BoxedSystem {
let system = camera_node_system.system().config(|config| { let system = camera_node_system.config(|config| {
config.0 = Some(CameraNodeState { config.0 = Some(CameraNodeState {
camera_name: self.camera_name.clone(), camera_name: self.camera_name.clone(),
command_queue: self.command_queue.clone(), command_queue: self.command_queue.clone(),

View file

@ -13,8 +13,9 @@ use bevy_app::EventReader;
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId}; use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
prelude::QueryState,
query::{Changed, Or, With}, query::{Changed, Or, With},
system::{BoxedSystem, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res, ResMut}, system::{BoxedSystem, ConfigurableSystem, Local, QuerySet, RemovedComponents, Res, ResMut},
world::World, world::World,
}; };
use bevy_utils::HashMap; use bevy_utils::HashMap;
@ -400,7 +401,7 @@ where
T: renderer::RenderResources, T: renderer::RenderResources,
{ {
fn get_system(&self) -> BoxedSystem { fn get_system(&self) -> BoxedSystem {
let system = render_resources_node_system::<T>.system().config(|config| { let system = render_resources_node_system::<T>.config(|config| {
config.0 = Some(RenderResourcesNodeState { config.0 = Some(RenderResourcesNodeState {
command_queue: self.command_queue.clone(), command_queue: self.command_queue.clone(),
uniform_buffer_arrays: UniformBufferArrays::<Entity, T>::default(), uniform_buffer_arrays: UniformBufferArrays::<Entity, T>::default(),
@ -435,8 +436,11 @@ fn render_resources_node_system<T: RenderResources>(
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,
removed: RemovedComponents<T>, removed: RemovedComponents<T>,
mut queries: QuerySet<( mut queries: QuerySet<(
Query<(Entity, &T, &Visible, &mut RenderPipelines), Or<(Changed<T>, Changed<Visible>)>>, QueryState<
Query<(Entity, &T, &Visible, &mut RenderPipelines)>, (Entity, &T, &Visible, &mut RenderPipelines),
Or<(Changed<T>, Changed<Visible>)>,
>,
QueryState<(Entity, &T, &Visible, &mut RenderPipelines)>,
)>, )>,
) { ) {
let state = state.deref_mut(); let state = state.deref_mut();
@ -444,7 +448,7 @@ fn render_resources_node_system<T: RenderResources>(
let render_resource_context = &**render_resource_context; let render_resource_context = &**render_resource_context;
uniform_buffer_arrays.begin_update(); uniform_buffer_arrays.begin_update();
// initialize uniform buffer arrays using the first RenderResources // 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); uniform_buffer_arrays.initialize(first, render_resource_context);
} }
@ -454,8 +458,7 @@ fn render_resources_node_system<T: RenderResources>(
// handle entities that were waiting for texture loads on the last update // handle entities that were waiting for texture loads on the last update
for entity in std::mem::take(&mut *entities_waiting_for_textures) { for entity in std::mem::take(&mut *entities_waiting_for_textures) {
if let Ok((entity, uniforms, _visible, mut render_pipelines)) = if let Ok((entity, uniforms, _visible, mut render_pipelines)) = queries.q1().get_mut(entity)
queries.q1_mut().get_mut(entity)
{ {
if !setup_uniform_texture_resources::<T>( if !setup_uniform_texture_resources::<T>(
uniforms, uniforms,
@ -467,7 +470,7 @@ fn render_resources_node_system<T: RenderResources>(
} }
} }
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 { if !visible.is_visible {
continue; continue;
} }
@ -496,8 +499,7 @@ fn render_resources_node_system<T: RenderResources>(
// if the buffer array was resized, write all entities to the new buffer, otherwise // if the buffer array was resized, write all entities to the new buffer, otherwise
// only write changes // only write changes
if resized { if resized {
for (entity, uniforms, visible, mut render_pipelines) in for (entity, uniforms, visible, mut render_pipelines) in queries.q1().iter_mut()
queries.q1_mut().iter_mut()
{ {
if !visible.is_visible { if !visible.is_visible {
continue; continue;
@ -513,8 +515,7 @@ fn render_resources_node_system<T: RenderResources>(
); );
} }
} else { } else {
for (entity, uniforms, visible, mut render_pipelines) in for (entity, uniforms, visible, mut render_pipelines) in queries.q0().iter_mut()
queries.q0_mut().iter_mut()
{ {
if !visible.is_visible { if !visible.is_visible {
continue; continue;
@ -583,15 +584,13 @@ where
T: renderer::RenderResources + Asset, T: renderer::RenderResources + Asset,
{ {
fn get_system(&self) -> BoxedSystem { fn get_system(&self) -> BoxedSystem {
let system = asset_render_resources_node_system::<T> let system = asset_render_resources_node_system::<T>.config(|config| {
.system() config.0 = Some(RenderResourcesNodeState {
.config(|config| { command_queue: self.command_queue.clone(),
config.0 = Some(RenderResourcesNodeState { uniform_buffer_arrays: UniformBufferArrays::<HandleId, T>::default(),
command_queue: self.command_queue.clone(), dynamic_uniforms: self.dynamic_uniforms,
uniform_buffer_arrays: UniformBufferArrays::<HandleId, T>::default(), })
dynamic_uniforms: self.dynamic_uniforms, });
})
});
Box::new(system) Box::new(system)
} }
@ -621,8 +620,8 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
render_resource_context: Res<Box<dyn RenderResourceContext>>, render_resource_context: Res<Box<dyn RenderResourceContext>>,
removed_handles: RemovedComponents<Handle<T>>, removed_handles: RemovedComponents<Handle<T>>,
mut queries: QuerySet<( mut queries: QuerySet<(
Query<(&Handle<T>, &mut RenderPipelines), Changed<Handle<T>>>, QueryState<(&Handle<T>, &mut RenderPipelines), Changed<Handle<T>>>,
Query<&mut RenderPipelines, With<Handle<T>>>, QueryState<&mut RenderPipelines, With<Handle<T>>>,
)>, )>,
) { ) {
let state = state.deref_mut(); let state = state.deref_mut();
@ -752,7 +751,7 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
// update removed entity asset mapping // update removed entity asset mapping
for entity in removed_handles.iter() { 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 render_pipelines
.bindings .bindings
.remove_asset_with_type(TypeId::of::<T>()) .remove_asset_with_type(TypeId::of::<T>())
@ -760,7 +759,7 @@ fn asset_render_resources_node_system<T: RenderResources + Asset>(
} }
// update changed entity asset mapping // 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 render_pipelines
.bindings .bindings
.remove_asset_with_type(TypeId::of::<T>()); .remove_asset_with_type(TypeId::of::<T>());

View file

@ -151,7 +151,19 @@ fn reflect_binding(
filtering: true, 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 { let shader_stage = match shader_stage {

View file

@ -8,9 +8,9 @@ use crate::{
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::{Assets, Handle, HandleUntyped}; use bevy_asset::{Assets, Handle, HandleUntyped};
use bevy_ecs::{ use bevy_ecs::{
query::With, query::{QueryState, With},
reflect::ReflectComponent, reflect::ReflectComponent,
system::{IntoSystem, Query, QuerySet, Res}, system::{QuerySet, Res},
world::Mut, world::Mut,
}; };
use bevy_reflect::{Reflect, TypeUuid}; use bevy_reflect::{Reflect, TypeUuid};
@ -27,7 +27,7 @@ pub struct WireframePlugin;
impl Plugin for WireframePlugin { impl Plugin for WireframePlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<WireframeConfig>() app.init_resource::<WireframeConfig>()
.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 world = app.world.cell();
let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap(); let mut shaders = world.get_resource_mut::<Assets<Shader>>().unwrap();
let mut pipelines = world let mut pipelines = world
@ -62,8 +62,8 @@ pub fn draw_wireframes_system(
meshes: Res<Assets<Mesh>>, meshes: Res<Assets<Mesh>>,
wireframe_config: Res<WireframeConfig>, wireframe_config: Res<WireframeConfig>,
mut query: QuerySet<( mut query: QuerySet<(
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>, QueryState<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible)>,
Query<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>, QueryState<(&mut Draw, &mut RenderPipelines, &Handle<Mesh>, &Visible), With<Wireframe>>,
)>, )>,
) { ) {
let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): ( let iterator = |(mut draw, mut render_pipelines, mesh_handle, visible): (
@ -123,8 +123,8 @@ pub fn draw_wireframes_system(
}; };
if wireframe_config.global { if wireframe_config.global {
query.q0_mut().iter_mut().for_each(iterator); query.q0().iter_mut().for_each(iterator);
} else { } else {
query.q1_mut().iter_mut().for_each(iterator); query.q1().iter_mut().for_each(iterator);
} }
} }

View file

@ -2,10 +2,6 @@
name = "bevy_text" name = "bevy_text"
version = "0.5.0" version = "0.5.0"
edition = "2018" edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "Provides text functionality for Bevy Engine" description = "Provides text functionality for Bevy Engine"
homepage = "https://bevyengine.org" homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy" repository = "https://github.com/bevyengine/bevy"

View file

@ -29,7 +29,7 @@ pub mod prelude {
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_asset::AddAsset; use bevy_asset::AddAsset;
use bevy_ecs::{entity::Entity, system::IntoSystem}; use bevy_ecs::entity::Entity;
use bevy_render::RenderStage; use bevy_render::RenderStage;
pub type DefaultTextPipeline = TextPipeline<Entity>; pub type DefaultTextPipeline = TextPipeline<Entity>;
@ -43,7 +43,7 @@ impl Plugin for TextPlugin {
.add_asset::<FontAtlasSet>() .add_asset::<FontAtlasSet>()
.init_asset_loader::<FontLoader>() .init_asset_loader::<FontLoader>()
.insert_resource(DefaultTextPipeline::default()) .insert_resource(DefaultTextPipeline::default())
.add_system_to_stage(CoreStage::PostUpdate, text2d_system.system()) .add_system_to_stage(CoreStage::PostUpdate, text2d_system)
.add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system.system()); .add_system_to_stage(RenderStage::Draw, text2d::draw_text2d_system);
} }
} }

View file

@ -2,7 +2,7 @@ use bevy_asset::Assets;
use bevy_ecs::{ use bevy_ecs::{
bundle::Bundle, bundle::Bundle,
entity::Entity, entity::Entity,
query::{Changed, With, Without}, query::{Changed, QueryState, With, Without},
system::{Local, Query, QuerySet, Res, ResMut}, system::{Local, Query, QuerySet, Res, ResMut},
}; };
use bevy_math::{Size, Vec3}; use bevy_math::{Size, Vec3};
@ -136,12 +136,12 @@ pub fn text2d_system(
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>, mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>, mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<( mut text_queries: QuerySet<(
Query<Entity, (With<MainPass>, Changed<Text>)>, QueryState<Entity, (With<MainPass>, Changed<Text>)>,
Query<(&Text, &mut Text2dSize), With<MainPass>>, QueryState<(&Text, &mut Text2dSize), With<MainPass>>,
)>, )>,
) { ) {
// Adds all entities where the text or the style has changed to the local queue // 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); queued_text.entities.push(entity);
} }
@ -157,7 +157,7 @@ pub fn text2d_system(
// Computes all text in the local queue // Computes all text in the local queue
let mut new_queue = Vec::new(); 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(..) { for entity in queued_text.entities.drain(..) {
if let Ok((text, mut calculated_size)) = query.get_mut(entity) { if let Ok((text, mut calculated_size)) = query.get_mut(entity) {
match text_pipeline.queue_text( match text_pipeline.queue_text(

View file

@ -2,10 +2,6 @@
name = "bevy_ui" name = "bevy_ui"
version = "0.5.0" version = "0.5.0"
edition = "2018" edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "A custom ECS-driven UI framework built specifically for Bevy Engine" description = "A custom ECS-driven UI framework built specifically for Bevy Engine"
homepage = "https://bevyengine.org" homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy" repository = "https://github.com/bevyengine/bevy"

View file

@ -114,8 +114,8 @@ impl From<Direction> for stretch::style::Direction {
fn from(value: Direction) -> Self { fn from(value: Direction) -> Self {
match value { match value {
Direction::Inherit => stretch::style::Direction::Inherit, Direction::Inherit => stretch::style::Direction::Inherit,
Direction::Ltr => stretch::style::Direction::LTR, Direction::LeftToRight => stretch::style::Direction::LTR,
Direction::Rtl => stretch::style::Direction::RTL, Direction::RightToLeft => stretch::style::Direction::RTL,
} }
} }
} }

View file

@ -22,10 +22,7 @@ pub mod prelude {
} }
use bevy_app::prelude::*; use bevy_app::prelude::*;
use bevy_ecs::{ use bevy_ecs::schedule::{ParallelSystemDescriptorCoercion, SystemLabel};
schedule::{ParallelSystemDescriptorCoercion, SystemLabel},
system::IntoSystem,
};
use bevy_input::InputSystem; use bevy_input::InputSystem;
use bevy_math::{Rect, Size}; use bevy_math::{Rect, Size};
use bevy_render::RenderStage; use bevy_render::RenderStage;
@ -62,35 +59,30 @@ impl Plugin for UiPlugin {
.register_type::<Val>() .register_type::<Val>()
.add_system_to_stage( .add_system_to_stage(
CoreStage::PreUpdate, CoreStage::PreUpdate,
ui_focus_system ui_focus_system.label(UiSystem::Focus).after(InputSystem),
.system()
.label(UiSystem::Focus)
.after(InputSystem),
) )
// add these stages to front because these must run before transform update systems // add these stages to front because these must run before transform update systems
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
widget::text_system.system().before(UiSystem::Flex), widget::text_system.before(UiSystem::Flex),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
widget::image_node_system.system().before(UiSystem::Flex), widget::image_node_system.before(UiSystem::Flex),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
flex_node_system flex_node_system
.system()
.label(UiSystem::Flex) .label(UiSystem::Flex)
.before(TransformSystem::TransformPropagate), .before(TransformSystem::TransformPropagate),
) )
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
ui_z_system ui_z_system
.system()
.after(UiSystem::Flex) .after(UiSystem::Flex)
.before(TransformSystem::TransformPropagate), .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); crate::render::add_ui_graph(&mut app.world);
} }

View file

@ -155,8 +155,8 @@ impl Default for AlignContent {
#[reflect_value(PartialEq, Serialize, Deserialize)] #[reflect_value(PartialEq, Serialize, Deserialize)]
pub enum Direction { pub enum Direction {
Inherit, Inherit,
Ltr, LeftToRight,
Rtl, RightToLeft,
} }
impl Default for Direction { impl Default for Direction {

View file

@ -54,7 +54,7 @@ fn update_hierarchy(
mod tests { mod tests {
use bevy_ecs::{ use bevy_ecs::{
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
system::{CommandQueue, Commands, IntoSystem}, system::{CommandQueue, Commands},
world::World, world::World,
}; };
use bevy_transform::{components::Transform, hierarchy::BuildChildren}; use bevy_transform::{components::Transform, hierarchy::BuildChildren};
@ -122,7 +122,7 @@ mod tests {
let mut schedule = Schedule::default(); let mut schedule = Schedule::default();
let mut update_stage = SystemStage::parallel(); 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.add_stage("update", update_stage);
schedule.run(&mut world); schedule.run(&mut world);

View file

@ -2,7 +2,7 @@ use crate::{CalculatedSize, Node, Style, Val};
use bevy_asset::Assets; use bevy_asset::Assets;
use bevy_ecs::{ use bevy_ecs::{
entity::Entity, entity::Entity,
query::{Changed, Or, With, Without}, query::{Changed, Or, QueryState, With, Without},
system::{Local, Query, QuerySet, Res, ResMut}, system::{Local, Query, QuerySet, Res, ResMut},
}; };
use bevy_math::Size; use bevy_math::Size;
@ -53,9 +53,9 @@ pub fn text_system(
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>, mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>, mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<( mut text_queries: QuerySet<(
Query<Entity, Or<(Changed<Text>, Changed<Style>)>>, QueryState<Entity, Or<(Changed<Text>, Changed<Style>)>>,
Query<Entity, (With<Text>, With<Style>)>, QueryState<Entity, (With<Text>, With<Style>)>,
Query<(&Text, &Style, &mut CalculatedSize)>, QueryState<(&Text, &Style, &mut CalculatedSize)>,
)>, )>,
) { ) {
let scale_factor = if let Some(window) = windows.get_primary() { let scale_factor = if let Some(window) = windows.get_primary() {
@ -86,7 +86,7 @@ pub fn text_system(
// Computes all text in the local queue // Computes all text in the local queue
let mut new_queue = Vec::new(); let mut new_queue = Vec::new();
let query = text_queries.q2_mut(); let mut query = text_queries.q2();
for entity in queued_text.entities.drain(..) { for entity in queued_text.entities.drain(..) {
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) { if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
let node_size = Size::new( let node_size = Size::new(

View file

@ -2,6 +2,7 @@ use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
math::Quat, math::Quat,
prelude::*, prelude::*,
render::camera::Camera,
sprite::SpriteSettings, sprite::SpriteSettings,
}; };
@ -9,9 +10,6 @@ use rand::Rng;
const CAMERA_SPEED: f32 = 1000.0; const CAMERA_SPEED: f32 = 1000.0;
pub struct PrintTimer(Timer);
pub struct Position(Transform);
/// This example is for performance testing purposes. /// This example is for performance testing purposes.
/// See https://github.com/bevyengine/bevy/pull/1492 /// See https://github.com/bevyengine/bevy/pull/1492
fn main() { fn main() {
@ -24,8 +22,8 @@ fn main() {
}) })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup) .add_startup_system(setup)
.add_system(tick.system().label("Tick")) .add_system(tick_system.label("Tick"))
.add_system(move_camera.system().after("Tick")) .add_system(move_camera_system.after("Tick"))
.run() .run()
} }
@ -44,14 +42,15 @@ fn setup(
let sprite_handle = materials.add(assets.load("branding/icon.png").into()); let sprite_handle = materials.add(assets.load("branding/icon.png").into());
// Spawns the camera
commands commands
.spawn() .spawn()
.insert_bundle(OrthographicCameraBundle::new_2d()) .insert_bundle(OrthographicCameraBundle::new_2d())
.insert(PrintTimer(Timer::from_seconds(1.0, true))) .insert(Timer::from_seconds(1.0, true))
.insert(Position(Transform::from_translation(Vec3::new( .insert(Transform::from_xyz(0.0, 0.0, 1000.0));
0.0, 0.0, 1000.0,
))));
// Builds and spawns the sprites
let mut sprites = vec![];
for y in -half_y..half_y { for y in -half_y..half_y {
for x in -half_x..half_x { for x in -half_x..half_x {
let position = Vec2::new(x as f32, y as f32); let position = Vec2::new(x as f32, y as f32);
@ -59,7 +58,7 @@ fn setup(
let rotation = Quat::from_rotation_z(rng.gen::<f32>()); let rotation = Quat::from_rotation_z(rng.gen::<f32>());
let scale = Vec3::splat(rng.gen::<f32>() * 2.0); let scale = Vec3::splat(rng.gen::<f32>() * 2.0);
commands.spawn().insert_bundle(SpriteBundle { sprites.push(SpriteBundle {
material: sprite_handle.clone(), material: sprite_handle.clone(),
transform: Transform { transform: Transform {
translation, translation,
@ -71,26 +70,23 @@ fn setup(
}); });
} }
} }
commands.spawn_batch(sprites);
} }
fn move_camera(time: Res<Time>, mut query: Query<(&mut Transform, &mut Position)>) { // System for rotating and translating the camera
for (mut transform, mut position) in query.iter_mut() { fn move_camera_system(time: Res<Time>, mut camera_query: Query<&mut Transform, With<Camera>>) {
position let mut camera_transform = camera_query.single_mut();
.0 camera_transform.rotate(Quat::from_rotation_z(time.delta_seconds() * 0.5));
.rotate(Quat::from_rotation_z(time.delta_seconds() * 0.5)); *camera_transform = *camera_transform
position.0 = * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds());
position.0 * Transform::from_translation(Vec3::X * CAMERA_SPEED * time.delta_seconds()); }
transform.translation = position.0.translation;
transform.rotation *= Quat::from_rotation_z(time.delta_seconds() / 2.0); // System for printing the number of sprites on every tick of the timer
} fn tick_system(time: Res<Time>, sprites_query: Query<&Sprite>, mut timer_query: Query<&mut Timer>) {
} let mut timer = timer_query.single_mut();
timer.tick(time.delta());
fn tick(time: Res<Time>, sprites: Query<&Sprite>, mut query: Query<&mut PrintTimer>) {
for mut timer in query.iter_mut() { if timer.just_finished() {
timer.0.tick(time.delta()); info!("Sprites: {}", sprites_query.iter().count(),);
if timer.0.just_finished() {
info!("Sprites: {}", sprites.iter().count(),);
}
} }
} }

View file

@ -9,13 +9,13 @@ fn main() {
fn setup( fn setup(
mut commands: Commands, mut commands: Commands,
_asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
// mut materials: ResMut<Assets<ColorMaterial>>, mut materials: ResMut<Assets<ColorMaterial>>,
) { ) {
// let texture_handle = asset_server.load("branding/icon.png"); let texture_handle = asset_server.load("branding/icon.png");
commands.spawn_bundle(OrthographicCameraBundle::new_2d()); commands.spawn_bundle(OrthographicCameraBundle::new_2d());
commands.spawn_bundle(SpriteBundle { commands.spawn_bundle(SpriteBundle {
// material: materials.add(texture_handle.into()), material: materials.add(texture_handle.into()),
..Default::default() ..Default::default()
}); });
} }

View file

@ -4,7 +4,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -8,8 +8,8 @@ fn main() {
}) })
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(rotator_system.system()) .add_system(rotator_system)
.run(); .run();
} }

View file

@ -8,7 +8,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -4,7 +4,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -6,8 +6,8 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(rotator_system.system()) .add_system(rotator_system)
.run(); .run();
} }

View file

@ -5,7 +5,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -226,8 +226,8 @@ fn setup(
fn main() { fn main() {
let mut app = App::new(); let mut app = App::new();
app.add_plugins(DefaultPlugins) app.add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(cube_rotator_system.system()) .add_system(cube_rotator_system)
.add_system(rotator_system.system()) .add_system(rotator_system)
.run(); .run();
} }

View file

@ -154,11 +154,11 @@ fn toggle_light(
fn toggle_shadows( fn toggle_shadows(
mut commands: Commands, mut commands: Commands,
input: Res<Input<KeyCode>>, input: Res<Input<KeyCode>>,
queries: QuerySet<( mut queries: QuerySet<(
Query<Entity, (With<Handle<Mesh>>, With<NotShadowCaster>)>, QueryState<Entity, (With<Handle<Mesh>>, With<NotShadowCaster>)>,
Query<Entity, (With<Handle<Mesh>>, With<NotShadowReceiver>)>, QueryState<Entity, (With<Handle<Mesh>>, With<NotShadowReceiver>)>,
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>, QueryState<Entity, (With<Handle<Mesh>>, Without<NotShadowCaster>)>,
Query<Entity, (With<Handle<Mesh>>, Without<NotShadowReceiver>)>, QueryState<Entity, (With<Handle<Mesh>>, Without<NotShadowReceiver>)>,
)>, )>,
) { ) {
if input.just_pressed(KeyCode::C) { if input.just_pressed(KeyCode::C) {

View file

@ -17,8 +17,8 @@ fn main() {
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(FrameTimeDiagnosticsPlugin::default()) .add_plugin(FrameTimeDiagnosticsPlugin::default())
.add_plugin(LogDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(move_cubes.system()) .add_system(move_cubes)
.run(); .run();
} }

View file

@ -4,7 +4,7 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -4,10 +4,10 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.insert_resource(SceneInstance::default()) .init_resource::<SceneInstance>()
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(scene_update.system()) .add_system(scene_update)
.add_system(move_scene_entities.system()) .add_system(move_scene_entities)
.run(); .run();
} }

View file

@ -16,7 +16,7 @@ fn main() {
}) })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(WireframePlugin) .add_plugin(WireframePlugin)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -11,9 +11,9 @@ use bevy::{
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(rotator_system.system()) .add_system(rotator_system)
.add_system(camera_order_color_system.system()) .add_system(camera_order_color_system)
.run(); .run();
} }

View file

@ -171,7 +171,6 @@ Example | File | Description
`hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities `hierarchy` | [`ecs/hierarchy.rs`](./ecs/hierarchy.rs) | Creates a hierarchy of parents and children entities
`iter_combinations` | [`ecs/iter_combinations.rs`](./ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results. `iter_combinations` | [`ecs/iter_combinations.rs`](./ecs/iter_combinations.rs) | Shows how to iterate over combinations of query results.
`parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator` `parallel_query` | [`ecs/parallel_query.rs`](./ecs/parallel_query.rs) | Illustrates parallel queries with `ParallelIterator`
`query_bundle` | [`ecs/query_bundle.rs`](./ecs/query_bundle.rs) | Shows how to query entities that contain components in a `Bundle`
`removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame. `removal_detection` | [`ecs/removal_detection.rs`](./ecs/removal_detection.rs) | Query for entities that had a specific component removed in a previous stage during the current frame.
`startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up) `startup_system` | [`ecs/startup_system.rs`](./ecs/startup_system.rs) | Demonstrates a startup system (one that runs once when the app starts up)
`state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state `state` | [`ecs/state.rs`](./ecs/state.rs) | Illustrates how to use States to control transitioning from a Menu state to an InGame state

View file

@ -6,7 +6,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 2 }) .insert_resource(Msaa { samples: 2 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -24,6 +24,6 @@ fn main() {
App::new() App::new()
.insert_resource(Input(String::new())) .insert_resource(Input(String::new()))
.set_runner(my_runner) .set_runner(my_runner)
.add_system(print_system.system()) .add_system(print_system)
.run(); .run();
} }

View file

@ -3,7 +3,7 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_system(file_drag_and_drop_system.system()) .add_system(file_drag_and_drop_system)
.run(); .run();
} }

View file

@ -13,7 +13,7 @@ fn main() {
App::new() App::new()
.insert_resource(ScheduleRunnerSettings::run_once()) .insert_resource(ScheduleRunnerSettings::run_once())
.add_plugins(MinimalPlugins) .add_plugins(MinimalPlugins)
.add_system(hello_world_system.system()) .add_system(hello_world_system)
.run(); .run();
// this app loops forever at 60 fps // this app loops forever at 60 fps
@ -22,7 +22,7 @@ fn main() {
1.0 / 60.0, 1.0 / 60.0,
))) )))
.add_plugins(MinimalPlugins) .add_plugins(MinimalPlugins)
.add_system(counter.system()) .add_system(counter)
.run(); .run();
} }

View file

@ -9,7 +9,7 @@ fn main() {
// filter: "wgpu=warn,bevy_ecs=info".to_string(), // filter: "wgpu=warn,bevy_ecs=info".to_string(),
// }) // })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_system(log_system.system()) .add_system(log_system)
.run(); .run();
} }

View file

@ -28,8 +28,7 @@ impl Plugin for PrintMessagePlugin {
message: self.message.clone(), message: self.message.clone(),
timer: Timer::new(self.wait_duration, true), timer: Timer::new(self.wait_duration, true),
}; };
app.insert_resource(state) app.insert_resource(state).add_system(print_message_system);
.add_system(print_message_system.system());
} }
} }

View file

@ -29,7 +29,7 @@ pub struct PrintHelloPlugin;
impl Plugin for PrintHelloPlugin { impl Plugin for PrintHelloPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_system(print_hello_system.system()); app.add_system(print_hello_system);
} }
} }
@ -41,7 +41,7 @@ pub struct PrintWorldPlugin;
impl Plugin for PrintWorldPlugin { impl Plugin for PrintWorldPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_system(print_world_system.system()); app.add_system(print_world_system);
} }
} }

View file

@ -8,7 +8,7 @@ fn main() {
}) })
.insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.8))) .insert_resource(ClearColor(Color::rgb(0.2, 0.2, 0.8)))
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_system(system1.system()) .add_system(system1)
.run(); .run();
println!("Running another App."); println!("Running another App.");
App::new() App::new()
@ -19,7 +19,7 @@ fn main() {
.add_plugins_with(DefaultPlugins, |group| { .add_plugins_with(DefaultPlugins, |group| {
group.disable::<bevy::log::LogPlugin>() group.disable::<bevy::log::LogPlugin>()
}) })
.add_system(system2.system()) .add_system(system2)
.run(); .run();
println!("Done."); println!("Done.");
} }

View file

@ -5,7 +5,7 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -39,8 +39,8 @@ fn main() {
.init_resource::<State>() .init_resource::<State>()
.add_asset::<CustomAsset>() .add_asset::<CustomAsset>()
.init_asset_loader::<CustomAssetLoader>() .init_asset_loader::<CustomAssetLoader>()
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(print_on_load.system()) .add_system(print_on_load)
.run(); .run();
} }

View file

@ -85,7 +85,7 @@ fn main() {
// asset system are initialized correctly. // asset system are initialized correctly.
group.add_before::<bevy::asset::AssetPlugin, _>(CustomAssetIoPlugin) group.add_before::<bevy::asset::AssetPlugin, _>(CustomAssetIoPlugin)
}) })
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -6,7 +6,7 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -12,10 +12,10 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup_env.system()) .add_startup_system(setup_env)
.add_startup_system(add_assets.system()) .add_startup_system(add_assets)
.add_startup_system(spawn_tasks.system()) .add_startup_system(spawn_tasks)
.add_system(handle_tasks.system()) .add_system(handle_tasks)
.run(); .run();
} }

View file

@ -4,7 +4,7 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.run(); .run();
} }

View file

@ -10,8 +10,8 @@ fn main() {
// The "print diagnostics" plugin is optional. It just visualizes our diagnostics in the // The "print diagnostics" plugin is optional. It just visualizes our diagnostics in the
// console // console
.add_plugin(LogDiagnosticsPlugin::default()) .add_plugin(LogDiagnosticsPlugin::default())
.add_startup_system(setup_diagnostic_system.system()) .add_startup_system(setup_diagnostic_system)
.add_system(my_system.system()) .add_system(my_system)
.run(); .run();
} }

View file

@ -5,10 +5,10 @@ use rand::Rng;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(change_component.system()) .add_system(change_component)
.add_system(change_detection.system()) .add_system(change_detection)
.add_system(tracker_monitoring.system()) .add_system(tracker_monitoring)
.run(); .run();
} }

View file

@ -272,7 +272,7 @@ fn main() {
// that :) The plugin below runs our app's "system schedule" once every 5 seconds // that :) The plugin below runs our app's "system schedule" once every 5 seconds
// (configured above). // (configured above).
.add_plugin(ScheduleRunnerPlugin::default()) .add_plugin(ScheduleRunnerPlugin::default())
// Resources that implement the Default or FromResources trait can be added like this: // Resources that implement the Default or FromWorld trait can be added like this:
.init_resource::<GameState>() .init_resource::<GameState>()
// Startup systems run exactly once BEFORE all other systems. These are generally used for // Startup systems run exactly once BEFORE all other systems. These are generally used for
// app initialization code (ex: adding entities and resources) // app initialization code (ex: adding entities and resources)

View file

@ -7,8 +7,8 @@ fn main() {
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_event::<MyEvent>() .add_event::<MyEvent>()
.init_resource::<EventTriggerState>() .init_resource::<EventTriggerState>()
.add_system(event_trigger_system.system()) .add_system(event_trigger_system)
.add_system(event_listener_system.system()) .add_system(event_listener_system)
.run(); .run();
} }

View file

@ -12,7 +12,7 @@ fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
// this system will run once every update (it should match your screen's refresh rate) // this system will run once every update (it should match your screen's refresh rate)
.add_system(frame_update.system()) .add_system(frame_update)
// add a new stage that runs twice a second // add a new stage that runs twice a second
.add_stage_after( .add_stage_after(
CoreStage::Update, CoreStage::Update,
@ -24,7 +24,7 @@ fn main() {
// FixedTimestep state from within a system // FixedTimestep state from within a system
.with_label(LABEL), .with_label(LABEL),
) )
.with_system(fixed_update.system()), .with_system(fixed_update),
) )
.run(); .run();
} }

View file

@ -3,8 +3,8 @@ use bevy::prelude::*;
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(setup.system()) .add_startup_system(setup)
.add_system(rotate.system()) .add_system(rotate)
.run(); .run();
} }

View file

@ -10,14 +10,14 @@ fn main() {
App::new() App::new()
.insert_resource(Msaa { samples: 4 }) .insert_resource(Msaa { samples: 4 })
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(generate_bodies.system()) .add_startup_system(generate_bodies)
.add_stage_after( .add_stage_after(
CoreStage::Update, CoreStage::Update,
FixedUpdateStage, FixedUpdateStage,
SystemStage::parallel() SystemStage::parallel()
.with_run_criteria(FixedTimestep::step(DELTA_TIME)) .with_run_criteria(FixedTimestep::step(DELTA_TIME))
.with_system(interact_bodies.system()) .with_system(interact_bodies)
.with_system(integrate.system()), .with_system(integrate),
) )
.run(); .run();
} }

View file

@ -70,8 +70,8 @@ fn bounce_system(
fn main() { fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_startup_system(spawn_system.system()) .add_startup_system(spawn_system)
.add_system(move_system.system()) .add_system(move_system)
.add_system(bounce_system.system()) .add_system(bounce_system)
.run(); .run();
} }

Some files were not shown because too many files have changed in this diff Show more