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"
path = "examples/ecs/timers.rs"
[[example]]
name = "query_bundle"
path = "examples/ecs/query_bundle.rs"
# Games
[[example]]
name = "alien_cake_addict"

View file

@ -2,10 +2,6 @@
name = "bevy_ecs"
version = "0.5.0"
edition = "2018"
authors = [
"Bevy Contributors <bevyengine@gmail.com>",
"Carter Anderson <mcanders1@gmail.com>",
]
description = "Bevy Engine's entity component system"
homepage = "https://bevyengine.org"
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
// before moving on to the next Stage
schedule.add_stage("update", SystemStage::parallel()
.with_system(movement.system())
.with_system(movement)
);
// Run the schedule once. If your app has a "loop", you would run this once per loop

View file

@ -24,15 +24,11 @@ fn main() {
// Add systems to the Stage to execute our app logic
// We can label our systems to force a specific run-order between some of them
update.add_system(spawn_entities.system().label(SimulationSystem::Spawn));
update.add_system(
print_counter_when_changed
.system()
.after(SimulationSystem::Spawn),
);
update.add_system(age_all_entities.system().label(SimulationSystem::Age));
update.add_system(remove_old_entities.system().after(SimulationSystem::Age));
update.add_system(print_changed_entities.system().after(SimulationSystem::Age));
update.add_system(spawn_entities.label(SimulationSystem::Spawn));
update.add_system(print_counter_when_changed.after(SimulationSystem::Spawn));
update.add_system(age_all_entities.label(SimulationSystem::Age));
update.add_system(remove_old_entities.after(SimulationSystem::Age));
update.add_system(print_changed_entities.after(SimulationSystem::Age));
// Add the Stage with our systems to the Schedule
schedule.add_stage("update", update);

View file

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

View file

@ -16,13 +16,13 @@ fn main() {
// called "second". In "first" we update the events and in "second" we run our systems
// sending and receiving events.
let mut first = SystemStage::parallel();
first.add_system(Events::<MyEvent>::update_system.system());
first.add_system(Events::<MyEvent>::update_system);
schedule.add_stage("first", first);
// Add systems sending and receiving events to a "second" Stage
let mut second = SystemStage::parallel();
second.add_system(sending_system.system().label(EventSystem::Sending));
second.add_system(receiving_system.system().after(EventSystem::Sending));
second.add_system(sending_system.label(EventSystem::Sending));
second.add_system(receiving_system.after(EventSystem::Sending));
// Run the "second" Stage after the "first" Stage, so our Events always get updated before we use them
schedule.add_stage_after("first", "second", second);
@ -41,7 +41,6 @@ enum EventSystem {
}
// This is our event that we will send and receive in systems
#[derive(Debug)]
struct MyEvent {
pub message: String,
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
fn receiving_system(mut event_reader: EventReader<MyEvent>) {
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();
// Add systems to increase the counter and to print out the current value
update.add_system(increase_counter.system().label(CounterSystem::Increase));
update.add_system(print_counter.system().after(CounterSystem::Increase));
update.add_system(increase_counter.label(CounterSystem::Increase));
update.add_system(print_counter.after(CounterSystem::Increase));
schedule.add_stage("update", update);
for iteration in 1..=10 {

View file

@ -1,16 +1,16 @@
extern crate proc_macro;
use bevy_macro_utils::{derive_label, BevyManifest};
use bevy_macro_utils::BevyManifest;
use proc_macro::TokenStream;
use proc_macro2::Span;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream},
parse_macro_input,
punctuated::Punctuated,
token::Comma,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt,
Result, Token,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result,
Token,
};
struct AllTuples {
@ -110,15 +110,15 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
.map(|field| &field.ty)
.collect::<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_from_components = Vec::new();
for ((field_type, is_bundle), field) in
field_type.iter().zip(is_bundle.iter()).zip(field.iter())
{
if *is_bundle {
field_type_infos.push(quote! {
type_info.extend(<#field_type as #ecs_path::bundle::Bundle>::type_info());
field_component_ids.push(quote! {
component_ids.extend(<#field_type as #ecs_path::bundle::Bundle>::component_ids(components));
});
field_get_components.push(quote! {
self.#field.get_components(&mut func);
@ -127,8 +127,8 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
#field: <#field_type as #ecs_path::bundle::Bundle>::from_components(&mut func),
});
} else {
field_type_infos.push(quote! {
type_info.push(#ecs_path::component::TypeInfo::of::<#field_type>());
field_component_ids.push(quote! {
component_ids.push(components.get_or_insert_id::<#field_type>());
});
field_get_components.push(quote! {
func((&mut self.#field as *mut #field_type).cast::<u8>());
@ -147,10 +147,12 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
TokenStream::from(quote! {
/// SAFE: TypeInfo is returned in field-definition-order. [from_components] and [get_components] use field-definition-order
unsafe impl #impl_generics #ecs_path::bundle::Bundle for #struct_name#ty_generics #where_clause {
fn type_info() -> Vec<#ecs_path::component::TypeInfo> {
let mut type_info = Vec::with_capacity(#field_len);
#(#field_type_infos)*
type_info
fn component_ids(
components: &mut #ecs_path::component::Components,
) -> Vec<#ecs_path::component::ComponentId> {
let mut component_ids = Vec::with_capacity(#field_len);
#(#field_component_ids)*
component_ids
}
#[allow(unused_variables, unused_mut, non_snake_case)]
@ -174,38 +176,26 @@ fn get_idents(fmt_string: fn(usize) -> String, count: usize) -> Vec<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]
pub fn impl_query_set(_input: TokenStream) -> TokenStream {
let mut tokens = TokenStream::new();
let max_queries = 4;
let queries = get_idents(|i| format!("Q{}", i), max_queries);
let filters = get_idents(|i| format!("F{}", i), max_queries);
let lifetimes = get_lifetimes(|i| format!("'q{}", i), max_queries);
let state_lifetimes = get_lifetimes(|i| format!("'qs{}", i), max_queries);
let mut query_fns = Vec::new();
let mut query_fn_muts = Vec::new();
for i in 0..max_queries {
let query = &queries[i];
let filter = &filters[i];
let lifetime = &lifetimes[i];
let state_lifetime = &state_lifetimes[i];
let fn_name = Ident::new(&format!("q{}", i), Span::call_site());
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
let index = Index::from(i);
query_fns.push(quote! {
pub fn #fn_name(&self) -> &Query<#lifetime, #state_lifetime, #query, #filter> {
&self.0.#index
}
});
query_fn_muts.push(quote! {
pub fn #fn_name_mut(&mut self) -> &mut Query<#lifetime, #state_lifetime, #query, #filter> {
&mut self.0.#index
pub fn #fn_name(&mut self) -> Query<'_, '_, #query, #filter> {
// SAFE: systems run without conflicts with other systems.
// Conflicting queries in QuerySet are not accessible at the same time
// QuerySets are guaranteed to not conflict with other SystemParams
unsafe {
Query::new(self.world, &self.query_states.#index, self.last_change_tick, self.change_tick)
}
}
});
}
@ -213,12 +203,9 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
for query_count in 1..=max_queries {
let query = &queries[0..query_count];
let filter = &filters[0..query_count];
let lifetime = &lifetimes[0..query_count];
let state_lifetime = &state_lifetimes[0..query_count];
let query_fn = &query_fns[0..query_count];
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<'s, #(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, 's, #query, #filter>,)*)>
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
@ -271,10 +258,10 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
fn default_config() {}
}
impl<'s, 'w, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'s, 'w> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
type Item = QuerySet<(#(Query<'w, 's, #query, #filter>,)*)>;
type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>;
#[inline]
unsafe fn get_param(
@ -283,15 +270,18 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
world: &'w World,
change_tick: u32,
) -> Self::Item {
let (#(#query,)*) = &state.0;
QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*))
QuerySet {
query_states: &state.0,
world,
last_change_tick: system_meta.last_change_tick,
change_tick,
}
}
}
impl<#(#state_lifetime,)* #(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#lifetime, #state_lifetime, #query, #filter>,)*)>
impl<'w, 's, #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
#(#query_fn)*
#(#query_fn_mut)*
}
}));
@ -385,7 +375,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
TokenStream::from(quote! {
impl #impl_generics #path::system::SystemParam for #struct_name#ty_generics #where_clause {
type Fetch = #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents>;
type Fetch = #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents>;
}
#[doc(hidden)]
@ -416,7 +406,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}
}
impl #impl_generics #path::system::SystemParamFetch<'s, 'w> for #fetch_struct_name <(#(<#field_types as SystemParam>::Fetch,)*), #punctuated_generic_idents> {
impl #impl_generics #path::system::SystemParamFetch<'w, 's> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> {
type Item = #struct_name#ty_generics;
unsafe fn get_param(
state: &'s mut Self,
@ -425,7 +415,7 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
change_tick: u32,
) -> Self::Item {
#struct_name {
#(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
#(#fields: <<#field_types as #path::system::SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
#(#ignored_fields: <#ignored_field_types>::default(),)*
}
}
@ -436,43 +426,46 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
#[proc_macro_derive(SystemLabel)]
pub fn derive_system_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("SystemLabel").into());
derive_label(input, trait_path)
derive_label(input, Ident::new("SystemLabel", Span::call_site())).into()
}
#[proc_macro_derive(StageLabel)]
pub fn derive_stage_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path.segments.push(format_ident!("StageLabel").into());
derive_label(input, trait_path)
derive_label(input, Ident::new("StageLabel", Span::call_site())).into()
}
#[proc_macro_derive(AmbiguitySetLabel)]
pub fn derive_ambiguity_set_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("AmbiguitySetLabel").into());
derive_label(input, trait_path)
derive_label(input, Ident::new("AmbiguitySetLabel", Span::call_site())).into()
}
#[proc_macro_derive(RunCriteriaLabel)]
pub fn derive_run_criteria_label(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut trait_path = bevy_ecs_path();
trait_path.segments.push(format_ident!("schedule").into());
trait_path
.segments
.push(format_ident!("RunCriteriaLabel").into());
derive_label(input, trait_path)
derive_label(input, Ident::new("RunCriteriaLabel", Span::call_site())).into()
}
fn derive_label(input: DeriveInput, label_type: Ident) -> TokenStream2 {
let ident = input.ident;
let ecs_path: Path = bevy_ecs_path();
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut where_clause = where_clause.cloned().unwrap_or_else(|| syn::WhereClause {
where_token: Default::default(),
predicates: Default::default(),
});
where_clause.predicates.push(syn::parse2(quote! { Self: Eq + ::std::fmt::Debug + ::std::hash::Hash + Clone + Send + Sync + 'static }).unwrap());
quote! {
impl #impl_generics #ecs_path::schedule::#label_type for #ident #ty_generics #where_clause {
fn dyn_clone(&self) -> Box<dyn #ecs_path::schedule::#label_type> {
Box::new(Clone::clone(self))
}
}
}
}
fn bevy_ecs_path() -> syn::Path {

View file

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

View file

@ -1,10 +1,10 @@
pub use bevy_ecs_macros::Bundle;
use crate::{
archetype::ComponentStatus,
component::{Component, ComponentId, ComponentTicks, Components, StorageType, TypeInfo},
entity::Entity,
storage::{SparseSetIndex, SparseSets, Table},
archetype::{AddBundle, Archetype, ArchetypeId, Archetypes, ComponentStatus},
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
entity::{Entities, Entity, EntityLocation},
storage::{SparseSetIndex, SparseSets, Storages, Table},
};
use bevy_ecs_macros::all_tuples;
use std::{any::TypeId, collections::HashMap};
@ -12,8 +12,6 @@ use std::{any::TypeId, collections::HashMap};
/// An ordered collection of components, commonly used for spawning entities, and adding and
/// removing components in bulk.
///
/// In order to query for components in a bundle use [crate::query::WithBundle].
///
/// Typically, you will simply use `#[derive(Bundle)]` when creating your own `Bundle`.
/// The `Bundle` trait is automatically implemented for tuples of components:
/// `(ComponentA, ComponentB)` is a very convenient shorthand when working with one-off collections
@ -38,13 +36,13 @@ use std::{any::TypeId, collections::HashMap};
/// ```
///
/// # Safety
/// [Bundle::type_info] must return the TypeInfo for each component type in the bundle, in the
/// [Bundle::component_id] must return the ComponentId for each component type in the bundle, in the
/// _exact_ order that [Bundle::get_components] is called.
/// [Bundle::from_components] must call `func` exactly once for each [TypeInfo] returned by
/// [Bundle::type_info]
/// [Bundle::from_components] must call `func` exactly once for each [ComponentId] returned by
/// [Bundle::component_id]
pub unsafe trait Bundle: Send + Sync + 'static {
/// Gets this [Bundle]'s components type info, in the order of this bundle's Components
fn type_info() -> Vec<TypeInfo>;
/// Gets this [Bundle]'s component ids, in the order of this bundle's Components
fn component_ids(components: &mut Components) -> Vec<ComponentId>;
/// Calls `func`, which should return data for each component in the bundle, in the order of
/// this bundle's Components
@ -66,8 +64,9 @@ macro_rules! tuple_impl {
($($name: ident),*) => {
/// SAFE: TypeInfo is returned in tuple-order. [Bundle::from_components] and [Bundle::get_components] use tuple-order
unsafe impl<$($name: Component),*> Bundle for ($($name,)*) {
fn type_info() -> Vec<TypeInfo> {
vec![$(TypeInfo::of::<$name>()),*]
#[allow(unused_variables)]
fn component_ids(components: &mut Components) -> Vec<ComponentId> {
vec![$(components.get_or_insert_id::<$name>()),*]
}
#[allow(unused_variables, unused_mut)]
@ -123,19 +122,119 @@ pub struct BundleInfo {
}
impl BundleInfo {
/// # Safety
/// table row must exist, entity must be valid
#[allow(clippy::too_many_arguments)]
#[inline]
pub(crate) unsafe fn write_components<T: Bundle>(
&self,
sparse_sets: &mut SparseSets,
entity: Entity,
table: &mut Table,
table_row: usize,
bundle_status: &[ComponentStatus],
bundle: T,
pub fn id(&self) -> BundleId {
self.id
}
#[inline]
pub fn components(&self) -> &[ComponentId] {
&self.component_ids
}
#[inline]
pub fn storage_types(&self) -> &[StorageType] {
&self.storage_types
}
pub(crate) fn get_bundle_inserter<'a, 'b>(
&'b self,
entities: &'a mut Entities,
archetypes: &'a mut Archetypes,
components: &mut Components,
storages: &'a mut Storages,
archetype_id: ArchetypeId,
change_tick: u32,
) -> BundleInserter<'a, 'b> {
let new_archetype_id =
self.add_bundle_to_archetype(archetypes, storages, components, archetype_id);
let archetypes_ptr = archetypes.archetypes.as_mut_ptr();
if new_archetype_id == archetype_id {
let archetype = &mut archetypes[archetype_id];
let table_id = archetype.table_id();
BundleInserter {
bundle_info: self,
archetype,
entities,
sparse_sets: &mut storages.sparse_sets,
table: &mut storages.tables[table_id],
archetypes_ptr,
change_tick,
result: InsertBundleResult::SameArchetype,
}
} else {
let (archetype, new_archetype) = archetypes.get_2_mut(archetype_id, new_archetype_id);
let table_id = archetype.table_id();
if table_id == new_archetype.table_id() {
BundleInserter {
bundle_info: self,
archetype,
archetypes_ptr,
entities,
sparse_sets: &mut storages.sparse_sets,
table: &mut storages.tables[table_id],
change_tick,
result: InsertBundleResult::NewArchetypeSameTable { new_archetype },
}
} else {
let (table, new_table) = storages
.tables
.get_2_mut(table_id, new_archetype.table_id());
BundleInserter {
bundle_info: self,
archetype,
sparse_sets: &mut storages.sparse_sets,
entities,
archetypes_ptr,
table,
change_tick,
result: InsertBundleResult::NewArchetypeNewTable {
new_archetype,
new_table,
},
}
}
}
}
pub(crate) fn get_bundle_spawner<'a, 'b>(
&'b self,
entities: &'a mut Entities,
archetypes: &'a mut Archetypes,
components: &mut Components,
storages: &'a mut Storages,
change_tick: u32,
) -> BundleSpawner<'a, 'b> {
let new_archetype_id =
self.add_bundle_to_archetype(archetypes, storages, components, ArchetypeId::EMPTY);
let (empty_archetype, archetype) =
archetypes.get_2_mut(ArchetypeId::EMPTY, new_archetype_id);
let table = &mut storages.tables[archetype.table_id()];
let add_bundle = empty_archetype.edges().get_add_bundle(self.id()).unwrap();
BundleSpawner {
archetype,
add_bundle,
bundle_info: self,
table,
entities,
sparse_sets: &mut storages.sparse_sets,
change_tick,
}
}
/// # Safety
/// `table` 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".
// bundle_info.component_ids are also in "bundle order"
@ -145,7 +244,7 @@ impl BundleInfo {
match self.storage_types[bundle_component] {
StorageType::Table => {
let column = table.get_column_mut(component_id).unwrap();
match bundle_status.get_unchecked(bundle_component) {
match add_bundle.bundle_status.get_unchecked(bundle_component) {
ComponentStatus::Added => {
column.initialize(
table_row,
@ -167,19 +266,277 @@ impl BundleInfo {
});
}
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
/// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change.
/// Results are cached in the Archetype Graph to avoid redundant work.
pub(crate) fn add_bundle_to_archetype(
&self,
archetypes: &mut Archetypes,
storages: &mut Storages,
components: &mut Components,
archetype_id: ArchetypeId,
) -> ArchetypeId {
if let Some(add_bundle) = archetypes[archetype_id].edges().get_add_bundle(self.id) {
return add_bundle.archetype_id;
}
let mut new_table_components = Vec::new();
let mut new_sparse_set_components = Vec::new();
let mut bundle_status = Vec::with_capacity(self.component_ids.len());
let current_archetype = &mut archetypes[archetype_id];
for component_id in self.component_ids.iter().cloned() {
if current_archetype.contains(component_id) {
bundle_status.push(ComponentStatus::Mutated);
} else {
bundle_status.push(ComponentStatus::Added);
// SAFE: component_id exists
let component_info = unsafe { components.get_info_unchecked(component_id) };
match component_info.storage_type() {
StorageType::Table => new_table_components.push(component_id),
StorageType::SparseSet => {
storages.sparse_sets.get_or_insert(component_info);
new_sparse_set_components.push(component_id)
}
}
}
}
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
let edges = current_archetype.edges_mut();
// the archetype does not change when we add this bundle
edges.insert_add_bundle(self.id, archetype_id, bundle_status);
archetype_id
} else {
let table_id;
let table_components;
let sparse_set_components;
// the archetype changes when we add this bundle. prepare the new archetype and storages
{
let current_archetype = &archetypes[archetype_id];
table_components = if new_table_components.is_empty() {
// if there are no new table components, we can keep using this table
table_id = current_archetype.table_id();
current_archetype.table_components().to_vec()
} else {
new_table_components.extend(current_archetype.table_components());
// sort to ignore order while hashing
new_table_components.sort();
// SAFE: all component ids in `new_table_components` exist
table_id = unsafe {
storages
.tables
.get_id_or_insert(&new_table_components, components)
};
new_table_components
};
sparse_set_components = if new_sparse_set_components.is_empty() {
current_archetype.sparse_set_components().to_vec()
} else {
new_sparse_set_components.extend(current_archetype.sparse_set_components());
// sort to ignore order while hashing
new_sparse_set_components.sort();
new_sparse_set_components
};
};
let new_archetype_id =
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
// add an edge from the old archetype to the new archetype
archetypes[archetype_id].edges_mut().insert_add_bundle(
self.id,
new_archetype_id,
bundle_status,
);
new_archetype_id
}
}
}
pub(crate) struct BundleInserter<'a, 'b> {
pub(crate) archetype: &'a mut Archetype,
pub(crate) entities: &'a mut Entities,
bundle_info: &'b BundleInfo,
table: &'a mut Table,
sparse_sets: &'a mut SparseSets,
result: InsertBundleResult<'a>,
archetypes_ptr: *mut Archetype,
change_tick: u32,
}
pub(crate) enum InsertBundleResult<'a> {
SameArchetype,
NewArchetypeSameTable {
new_archetype: &'a mut Archetype,
},
NewArchetypeNewTable {
new_archetype: &'a mut Archetype,
new_table: &'a mut Table,
},
}
impl<'a, 'b> BundleInserter<'a, 'b> {
/// # Safety
/// `entity` must currently exist in the source archetype for this inserter. `archetype_index` must be `entity`'s location in the archetype.
/// `T` must match this BundleInfo's type
#[inline]
pub fn id(&self) -> BundleId {
self.id
pub unsafe fn insert<T: Bundle>(
&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]
pub fn components(&self) -> &[ComponentId] {
&self.component_ids
}
#[inline]
pub fn storage_types(&self) -> &[StorageType] {
&self.storage_types
pub unsafe fn spawn<T: Bundle>(&mut self, bundle: T) -> Entity {
let entity = self.entities.alloc();
// SAFE: entity is allocated (but non-existent), `T` matches this BundleInfo's type
self.spawn_non_existent(entity, bundle);
entity
}
}
@ -206,10 +563,12 @@ impl Bundles {
) -> &'a BundleInfo {
let bundle_infos = &mut self.bundle_infos;
let id = self.bundle_ids.entry(TypeId::of::<T>()).or_insert_with(|| {
let type_info = T::type_info();
let component_ids = T::component_ids(components);
let id = BundleId(bundle_infos.len());
let bundle_info =
initialize_bundle(std::any::type_name::<T>(), &type_info, id, components);
// SAFE: T::component_id ensures info was created
let bundle_info = unsafe {
initialize_bundle(std::any::type_name::<T>(), component_ids, id, components)
};
bundle_infos.push(bundle_info);
id
});
@ -218,21 +577,21 @@ impl Bundles {
}
}
fn initialize_bundle(
/// # Safety
///
/// `component_id` must be valid [ComponentId]'s
unsafe fn initialize_bundle(
bundle_type_name: &'static str,
type_info: &[TypeInfo],
component_ids: Vec<ComponentId>,
id: BundleId,
components: &mut Components,
) -> BundleInfo {
let mut component_ids = Vec::new();
let mut storage_types = Vec::new();
for type_info in type_info {
let component_id = components.get_or_insert_with(type_info.type_id(), || type_info.clone());
// SAFE: get_with_type_info ensures info was created
let info = unsafe { components.get_info_unchecked(component_id) };
component_ids.push(component_id);
storage_types.push(info.storage_type());
for &component_id in &component_ids {
// SAFE: component_id exists and is therefore valid
let component_info = components.get_info_unchecked(component_id);
storage_types.push(component_info.storage_type());
}
let mut deduped = component_ids.clone();

View file

@ -1,7 +1,3 @@
mod type_info;
pub use type_info::*;
use crate::storage::SparseSetIndex;
use std::{
alloc::Layout,
@ -56,15 +52,8 @@ impl Default for StorageType {
#[derive(Debug)]
pub struct ComponentInfo {
name: String,
id: ComponentId,
type_id: Option<TypeId>,
// SAFETY: This must remain private. It must only be set to "true" if this component is
// actually Send + Sync
is_send_and_sync: bool,
layout: Layout,
drop: unsafe fn(*mut u8),
storage_type: StorageType,
descriptor: ComponentDescriptor,
}
impl ComponentInfo {
@ -75,44 +64,36 @@ impl ComponentInfo {
#[inline]
pub fn name(&self) -> &str {
&self.name
&self.descriptor.name
}
#[inline]
pub fn type_id(&self) -> Option<TypeId> {
self.type_id
self.descriptor.type_id
}
#[inline]
pub fn layout(&self) -> Layout {
self.layout
self.descriptor.layout
}
#[inline]
pub fn drop(&self) -> unsafe fn(*mut u8) {
self.drop
self.descriptor.drop
}
#[inline]
pub fn storage_type(&self) -> StorageType {
self.storage_type
self.descriptor.storage_type
}
#[inline]
pub fn is_send_and_sync(&self) -> bool {
self.is_send_and_sync
self.descriptor.is_send_and_sync
}
fn new(id: ComponentId, descriptor: ComponentDescriptor) -> Self {
ComponentInfo {
id,
name: descriptor.name,
storage_type: descriptor.storage_type,
type_id: descriptor.type_id,
is_send_and_sync: descriptor.is_send_and_sync,
drop: descriptor.drop,
layout: descriptor.layout,
}
ComponentInfo { id, descriptor }
}
}
@ -142,6 +123,7 @@ impl SparseSetIndex for ComponentId {
}
}
#[derive(Debug)]
pub struct ComponentDescriptor {
name: String,
storage_type: StorageType,
@ -154,6 +136,11 @@ pub struct ComponentDescriptor {
}
impl ComponentDescriptor {
// SAFETY: The pointer points to a valid value of type `T` and it is safe to drop this value.
unsafe fn drop_ptr<T>(x: *mut u8) {
x.cast::<T>().drop_in_place()
}
pub fn new<T: Component>(storage_type: StorageType) -> Self {
Self {
name: std::any::type_name::<T>().to_string(),
@ -161,7 +148,18 @@ impl ComponentDescriptor {
is_send_and_sync: true,
type_id: Some(TypeId::of::<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)]
pub struct Components {
components: Vec<ComponentInfo>,
@ -231,7 +216,12 @@ impl Components {
#[inline]
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]
@ -279,42 +269,58 @@ impl Components {
#[inline]
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]
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]
fn get_or_insert_resource_with(
unsafe fn get_or_insert_resource_with(
&mut self,
type_id: TypeId,
func: impl FnOnce() -> TypeInfo,
func: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId {
let components = &mut self.components;
let index = self.resource_indices.entry(type_id).or_insert_with(|| {
let type_info = func();
let descriptor = func();
let index = components.len();
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
components.push(ComponentInfo::new(ComponentId(index), descriptor));
index
});
ComponentId(*index)
}
/// # Safety
///
/// The [`ComponentDescriptor`] must match the [`TypeId`]
#[inline]
pub(crate) fn get_or_insert_with(
pub(crate) unsafe fn get_or_insert_with(
&mut self,
type_id: TypeId,
func: impl FnOnce() -> TypeInfo,
func: impl FnOnce() -> ComponentDescriptor,
) -> ComponentId {
let components = &mut self.components;
let index = self.indices.entry(type_id).or_insert_with(|| {
let type_info = func();
let descriptor = func();
let index = components.len();
components.push(ComponentInfo::new(ComponentId(index), type_info.into()));
components.push(ComponentInfo::new(ComponentId(index), descriptor));
index
});

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

View file

@ -68,7 +68,7 @@ enum State {
/// [`Events::update`] exactly once per update/frame.
///
/// [`Events::update_system`] is a system that does this, typically intialized automatically using
/// [`AppBuilder::add_event`]. [EventReader]s are expected to read events from this collection at
/// [`App::add_event`]. [EventReader]s are expected to read events from this collection at
/// least once per loop/frame.
/// Events will persist across a single frame boundary and so ordering of event producers and
/// consumers is not critical (although poorly-planned ordering may cause accumulating lag).
@ -115,9 +115,9 @@ enum State {
/// An alternative call pattern would be to call [Events::update] manually across frames to control
/// when events are cleared.
/// This complicates consumption and risks ever-expanding memory usage if not cleaned up,
/// but can be done by adding your event as a resource instead of using [`AppBuilder::add_event`].
/// but can be done by adding your event as a resource instead of using [`App::add_event`].
///
/// [`AppBuilder::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.AppBuilder.html#method.add_event
/// [`App::add_event`]: https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event
#[derive(Debug)]
pub struct Events<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.
#[derive(SystemParam)]
pub struct EventReader<'s, 'w, T: Component> {
pub struct EventReader<'w, 's, T: Component> {
last_event_count: Local<'s, (usize, PhantomData<T>)>,
events: Res<'w, Events<T>>,
}
/// Sends events of type `T`.
#[derive(SystemParam)]
pub struct EventWriter<'s, 'w, T: Component> {
pub struct EventWriter<'w, 's, T: Component> {
events: ResMut<'w, Events<T>>,
// TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'s, 'w, T: Component> EventWriter<'s, 'w, T> {
impl<'w, 's, T: Component> EventWriter<'w, 's, T> {
pub fn send(&mut self, event: T) {
self.events.send(event);
}
@ -255,7 +254,7 @@ fn internal_event_reader<'a, T>(
}
}
impl<'s, 'w, T: Component> EventReader<'s, 'w, T> {
impl<'w, 's, T: Component> EventReader<'w, 's, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened
/// before now.

View file

@ -22,7 +22,7 @@ pub mod prelude {
change_detection::DetectChanges,
entity::Entity,
event::{EventReader, EventWriter},
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, WithBundle, Without},
query::{Added, ChangeTrackers, Changed, Or, QueryState, With, Without},
schedule::{
AmbiguitySetLabel, ExclusiveSystemDescriptorCoercion, ParallelSystemDescriptorCoercion,
RunCriteria, RunCriteriaDescriptorCoercion, RunCriteriaLabel, RunCriteriaPiping,
@ -41,7 +41,7 @@ mod tests {
use crate as bevy_ecs;
use crate::{
bundle::Bundle,
component::{Component, ComponentDescriptor, ComponentId, StorageType, TypeInfo},
component::{Component, ComponentDescriptor, ComponentId, StorageType},
entity::Entity,
query::{
Added, ChangeTrackers, Changed, FilterFetch, FilteredAccess, With, Without, WorldQuery,
@ -60,7 +60,9 @@ mod tests {
#[derive(Debug, PartialEq, Eq)]
struct A(usize);
#[derive(Debug, PartialEq, Eq)]
struct B(usize);
#[derive(Debug, PartialEq, Eq)]
struct C;
#[derive(Clone, Debug)]
@ -102,21 +104,26 @@ mod tests {
#[test]
fn bundle_derive() {
let mut world = World::new();
#[derive(Bundle, PartialEq, Debug)]
struct Foo {
x: &'static str,
y: i32,
}
assert_eq!(
<Foo as Bundle>::type_info(),
vec![TypeInfo::of::<&'static str>(), TypeInfo::of::<i32>(),]
);
let mut world = World::new();
world
.register_component(ComponentDescriptor::new::<i32>(StorageType::SparseSet))
.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 e2 = world.spawn().insert_bundle(("def", 456, true)).id();
assert_eq!(*world.get::<&str>(e1).unwrap(), "abc");
@ -146,12 +153,12 @@ mod tests {
}
assert_eq!(
<Nested as Bundle>::type_info(),
<Nested as Bundle>::component_ids(world.components_mut()),
vec![
TypeInfo::of::<usize>(),
TypeInfo::of::<&'static str>(),
TypeInfo::of::<i32>(),
TypeInfo::of::<u8>(),
world.components_mut().get_or_insert_id::<usize>(),
world.components_mut().get_or_insert_id::<&'static str>(),
world.components_mut().get_or_insert_id::<i32>(),
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"
);
}
#[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`].
#[derive(Clone)]
pub struct EntityFetch {
entities: *const Entity,
}
@ -573,6 +574,7 @@ impl<T: WorldQuery> WorldQuery for Option<T> {
}
/// The [`Fetch`] of `Option<T>`.
#[derive(Clone)]
pub struct OptionFetch<T> {
fetch: T,
matches: bool,
@ -807,6 +809,21 @@ pub struct ChangeTrackersFetch<T> {
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
unsafe impl<T> ReadOnlyFetch for ChangeTrackersFetch<T> {}

View file

@ -1,6 +1,5 @@
use crate::{
archetype::{Archetype, ArchetypeComponentId},
bundle::Bundle,
component::{Component, ComponentId, ComponentTicks, StorageType},
entity::Entity,
query::{Access, Fetch, FetchState, FilteredAccess, WorldQuery},
@ -12,7 +11,7 @@ use std::{cell::UnsafeCell, marker::PhantomData, ptr};
/// Extension trait for [`Fetch`] containing methods used by query filters.
/// This trait exists to allow "short circuit" behaviors for relevant query filter fetches.
pub trait FilterFetch: for<'world, 'state> Fetch<'world, 'state> {
pub trait FilterFetch: for<'w, 's> Fetch<'w, 's> {
/// # Safety
///
/// Must always be called _after_ [`Fetch::set_archetype`]. `archetype_index` must be in the range
@ -282,106 +281,6 @@ impl<'w, 's, T: Component> Fetch<'w, 's> for WithoutFetch<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.
///
/// 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},
world::World,
};
use std::{marker::PhantomData, mem::MaybeUninit};
use std::mem::MaybeUninit;
/// An [`Iterator`] over query results of a [`Query`](crate::system::Query).
///
@ -217,7 +217,7 @@ where
archetypes: &'w Archetypes,
query_state: &'s QueryState<Q, F>,
world: &'w World,
cursors: [QueryIterationCursor<'w, 's, Q, F>; K],
cursors: [QueryIterationCursor<'s, Q, F>; K],
}
impl<'w, 's, Q: WorldQuery, F: WorldQuery, const K: usize> QueryCombinationIter<'w, 's, Q, F, K>
@ -239,7 +239,7 @@ where
// There is no FromIterator on arrays, so instead initialize it manually with MaybeUninit
// TODO: use MaybeUninit::uninit_array if it stabilizes
let mut cursors: [MaybeUninit<QueryIterationCursor<'w, 's, Q, F>>; K] =
let mut cursors: [MaybeUninit<QueryIterationCursor<'s, Q, F>>; K] =
MaybeUninit::uninit().assume_init();
for (i, cursor) in cursors.iter_mut().enumerate() {
match i {
@ -259,8 +259,8 @@ where
}
// TODO: use MaybeUninit::array_assume_init if it stabilizes
let cursors: [QueryIterationCursor<'w, 's, Q, F>; K] =
(&cursors as *const _ as *const [QueryIterationCursor<'w, 's, Q, F>; K]).read();
let cursors: [QueryIterationCursor<'s, Q, F>; K] =
(&cursors as *const _ as *const [QueryIterationCursor<'s, Q, F>; K]).read();
QueryCombinationIter {
world,
@ -277,9 +277,9 @@ where
/// references to the same component, leading to unique reference aliasing.
///.
/// It is always safe for shared access.
unsafe fn fetch_next_aliased_unchecked(
unsafe fn fetch_next_aliased_unchecked<'a>(
&mut self,
) -> Option<[<Q::Fetch as Fetch<'w, 's>>::Item; K]>
) -> Option<[<Q::Fetch as Fetch<'a, 's>>::Item; K]>
where
Q::Fetch: Clone,
F::Fetch: Clone,
@ -309,7 +309,7 @@ where
}
// TODO: use MaybeUninit::uninit_array if it stabilizes
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'w, 's>>::Item>; K] =
let mut values: [MaybeUninit<<Q::Fetch as Fetch<'a, 's>>::Item>; K] =
MaybeUninit::uninit().assume_init();
for (value, cursor) in values.iter_mut().zip(&mut self.cursors) {
@ -317,15 +317,15 @@ where
}
// TODO: use MaybeUninit::array_assume_init if it stabilizes
let values: [<Q::Fetch as Fetch<'w, 's>>::Item; K] =
(&values as *const _ as *const [<Q::Fetch as Fetch<'w, 's>>::Item; K]).read();
let values: [<Q::Fetch as Fetch<'a, 's>>::Item; K] =
(&values as *const _ as *const [<Q::Fetch as Fetch<'a, 's>>::Item; K]).read();
Some(values)
}
/// Get next combination of queried components
#[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
Q::Fetch: Clone,
F::Fetch: Clone,
@ -400,7 +400,7 @@ impl<'w, 's, Q: WorldQuery> ExactSizeIterator for QueryIter<'w, 's, Q, ()> {
}
}
struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
struct QueryIterationCursor<'s, Q: WorldQuery, F: WorldQuery> {
table_id_iter: std::slice::Iter<'s, TableId>,
archetype_id_iter: std::slice::Iter<'s, ArchetypeId>,
fetch: Q::Fetch,
@ -408,10 +408,9 @@ struct QueryIterationCursor<'w, 's, Q: WorldQuery, F: WorldQuery> {
current_len: usize,
current_index: usize,
is_dense: bool,
marker: PhantomData<&'w usize>,
}
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'w, 's, Q, F>
impl<'s, Q: WorldQuery, F: WorldQuery> Clone for QueryIterationCursor<'s, Q, F>
where
Q::Fetch: Clone,
F::Fetch: Clone,
@ -425,12 +424,11 @@ where
current_len: self.current_len,
current_index: self.current_index,
is_dense: self.is_dense,
marker: PhantomData,
}
}
}
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F>
impl<'s, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'s, Q, F>
where
F::Fetch: FilterFetch,
{
@ -473,13 +471,12 @@ where
archetype_id_iter: query_state.matched_archetype_ids.iter(),
current_len: 0,
current_index: 0,
marker: PhantomData,
}
}
/// retrieve item returned from most recent `next` call again.
#[inline]
unsafe fn peek_last(&mut self) -> Option<<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.is_dense {
Some(self.fetch.table_fetch(self.current_index - 1))
@ -495,7 +492,7 @@ where
// QueryIter, QueryIterationCursor, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
// We can't currently reuse QueryIterationCursor in QueryIter for performance reasons. See #1763 for context.
#[inline(always)]
unsafe fn next(
unsafe fn next<'w>(
&mut self,
tables: &'w Tables,
archetypes: &'w Archetypes,

View file

@ -13,6 +13,7 @@ use bevy_tasks::TaskPool;
use fixedbitset::FixedBitSet;
use thiserror::Error;
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
pub struct QueryState<Q: WorldQuery, F: WorldQuery = ()>
where
F::Fetch: FilterFetch,
@ -35,6 +36,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F>
where
F::Fetch: FilterFetch,
{
/// Creates a new [`QueryState`] from a given [`World`] and inherits the result of `world.id()`.
pub fn new(world: &mut World) -> Self {
let fetch_state = <Q::State as FetchState>::init(world);
let filter_state = <F::State as FetchState>::init(world);
@ -68,6 +70,7 @@ where
state
}
/// Checks if the query is empty for the given [`World`], where the last change and current tick are given.
#[inline]
pub fn is_empty(&self, world: &World, last_change_tick: u32, change_tick: u32) -> bool {
// SAFE: the iterator is instantly consumed via `none_remaining` and the implementation of
@ -78,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) {
self.validate_world(world);
let archetypes = world.archetypes();
@ -98,6 +107,7 @@ where
}
}
/// Creates a new [`Archetype`].
pub fn new_archetype(&mut self, archetype: &Archetype) {
if self.fetch_state.matches_archetype(archetype)
&& self.filter_state.matches_archetype(archetype)
@ -121,6 +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]
pub fn get<'w, 's>(
&'s mut self,
@ -134,6 +147,17 @@ where
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]
pub fn get_manual<'w, 's>(
&'s self,
@ -155,16 +179,8 @@ where
}
}
#[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) }
}
/// Gets the query result for the given [`World`] and [`Entity`].
///
/// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -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
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
pub unsafe fn get_unchecked_manual<'w, 's>(
@ -219,6 +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]
pub fn iter<'w, 's>(&'s mut self, world: &'w World) -> QueryIter<'w, 's, Q, F>
where
@ -228,6 +251,13 @@ where
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]
pub fn iter_manual<'w, 's>(&'s self, world: &'w World) -> QueryIter<'w, 's, Q, F>
where
@ -240,12 +270,16 @@ where
}
}
#[inline]
pub fn iter_mut<'w, 's>(&'s mut self, world: &'w mut World) -> QueryIter<'w, 's, Q, F> {
// SAFETY: query has unique world access
unsafe { self.iter_unchecked(world) }
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
/// This can only be called for read-only queries.
///
/// For permutations of size K of query returning N results, you will get:
/// - if K == N: one permutation of all query results
/// - if K < N: all possible K-sized combinations of query results, without repetition
/// - if K > N: empty set (no K-sized combinations exist)
///
/// This can only be called for read-only queries, see [`Self::iter_combinations_mut`] for
/// write-queries.
#[inline]
pub fn iter_combinations<'w, 's, const K: usize>(
&'s mut self,
@ -258,6 +292,13 @@ where
unsafe { self.iter_combinations_unchecked(world) }
}
/// Iterates over all possible combinations of `K` query results for the given [`World`]
/// without repetition.
///
/// For permutations of size K of query returning N results, you will get:
/// - if K == N: one permutation of all query results
/// - if K < N: all possible K-sized combinations of query results, without repetition
/// - if K > N: empty set (no K-sized combinations exist)
#[inline]
pub fn iter_combinations_mut<'w, 's, const K: usize>(
&'s mut self,
@ -267,6 +308,8 @@ where
unsafe { self.iter_combinations_unchecked(world) }
}
/// Returns an [`Iterator`] over the query results for the given [`World`].
///
/// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -280,6 +323,10 @@ where
self.iter_unchecked_manual(world, world.last_change_tick(), world.read_change_tick())
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
/// given [`World`] without repetition.
/// This can only be called for read-only queries.
///
/// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
@ -297,7 +344,11 @@ where
)
}
/// Returns an [`Iterator`] for the given [`World`], where the last change and
/// the current change tick are given.
///
/// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
@ -312,7 +363,12 @@ where
QueryIter::new(world, self, last_change_tick, change_tick)
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results for the
/// given [`World`] without repetition.
/// This can only be called for read-only queries.
///
/// # Safety
///
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
@ -327,6 +383,10 @@ where
QueryCombinationIter::new(world, self, last_change_tick, change_tick)
}
/// Runs `func` on each query result for the given [`World`]. This is faster than the equivalent
/// iter() method, but cannot be chained like a normal [`Iterator`].
///
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
#[inline]
pub fn for_each<'w, 's>(
&'s mut self,
@ -341,6 +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]
pub fn for_each_mut<'w, 's>(
&'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
///
/// 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]
pub fn par_for_each<'w, 's>(
&'s mut self,
@ -388,6 +459,7 @@ where
}
}
/// Runs `func` on each query result in parallel using the given `task_pool`.
#[inline]
pub fn par_for_each_mut<'w, 's>(
&'s mut self,
@ -402,6 +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
///
/// 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
///
/// 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
///
/// 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 {
add_component: fn(&mut World, Entity, &dyn Reflect),
apply_component: fn(&mut World, Entity, &dyn Reflect),
remove_component: fn(&mut World, Entity),
reflect_component: fn(&World, Entity) -> Option<&dyn Reflect>,
reflect_component_mut: unsafe fn(&World, Entity) -> Option<ReflectMut>,
copy_component: fn(&World, &mut World, Entity, Entity),
@ -24,6 +25,10 @@ impl ReflectComponent {
(self.apply_component)(world, entity, component);
}
pub fn remove_component(&self, world: &mut World, entity: Entity) {
(self.remove_component)(world, entity);
}
pub fn reflect_component<'a>(
&self,
world: &'a World,
@ -83,6 +88,9 @@ impl<C: Component + Reflect + FromWorld> FromType<C> for ReflectComponent {
let mut component = world.get_mut::<C>(entity).unwrap();
component.apply(reflected_component);
},
remove_component: |world, entity| {
world.entity_mut(entity).remove::<C>();
},
copy_component: |source_world, destination_world, source_entity, destination_entity| {
let source_component = source_world.get::<C>(source_entity).unwrap();
let mut destination_component = C::from_world(destination_world);

View file

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

View file

@ -1,15 +1,115 @@
pub use bevy_ecs_macros::{AmbiguitySetLabel, RunCriteriaLabel, StageLabel, SystemLabel};
use bevy_utils::define_label;
use std::{
any::Any,
borrow::Cow,
fmt::Debug,
hash::{Hash, Hasher},
};
define_label!(StageLabel);
pub trait DynEq: Any {
fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &dyn DynEq) -> bool;
}
impl<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>;
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>;
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>;
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>;
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 crate::{
system::{IntoSystem, System},
world::World,
};
use crate::{system::System, world::World};
use bevy_utils::HashMap;
#[derive(Default)]
@ -79,7 +76,7 @@ impl Schedule {
&mut self,
system: S,
) -> &mut Self {
self.run_criteria.set(Box::new(system.system()));
self.run_criteria.set(Box::new(system));
self
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -6,7 +6,7 @@ use crate::{
entity::{Entities, Entity},
world::World,
};
use bevy_utils::tracing::debug;
use bevy_utils::tracing::{error, warn};
pub use command_queue::CommandQueue;
use std::marker::PhantomData;
@ -16,12 +16,12 @@ pub trait Command: Send + Sync + 'static {
}
/// A list of commands that will be run to modify a [`World`].
pub struct Commands<'s, 'w> {
pub struct Commands<'w, 's> {
queue: &'s mut CommandQueue,
entities: &'w Entities,
}
impl<'s, 'w> Commands<'s, 'w> {
impl<'w, 's> Commands<'w, 's> {
/// Create a new `Commands` from a queue and a world.
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
Self {
@ -50,7 +50,7 @@ impl<'s, 'w> Commands<'s, 'w> {
/// }
/// # example_system.system();
/// ```
pub fn spawn(&mut self) -> EntityCommands<'s, 'w, '_> {
pub fn spawn<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> {
let entity = self.entities.reserve_entity();
EntityCommands {
entity,
@ -58,7 +58,14 @@ impl<'s, 'w> Commands<'s, 'w> {
}
}
pub fn get_or_spawn(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
/// Returns an [EntityCommands] for the given `entity` (if it exists) or spawns one if it doesn't exist.
/// This will return [None] if the `entity` exists with a different generation.
///
/// # Note
/// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`Commands::spawn`].
/// This method should generally only be used for sharing entities across apps, and only when they have a
/// scheme worked out to share an ID space (which doesn't happen by default).
pub fn get_or_spawn<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
self.add(GetOrSpawn { entity });
EntityCommands {
entity,
@ -66,9 +73,8 @@ impl<'s, 'w> Commands<'s, 'w> {
}
}
// TODO: this is a hack to work around the "multiple worlds" limitations:
// Right now Commands must allocate entities from their "scheduled" world, but Commands might be applied to other worlds,
// such as the "render world"
/// Spawns a [Bundle] without pre-allocating an [Entity]. The [Entity] will be allocated when
/// this [Command] is applied.
pub fn spawn_and_forget(&mut self, bundle: impl Bundle) {
self.queue.push(Spawn { bundle })
}
@ -113,7 +119,7 @@ impl<'s, 'w> Commands<'s, 'w> {
/// }
/// # example_system.system();
/// ```
pub fn spawn_bundle<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();
e.insert_bundle(bundle);
e
@ -138,7 +144,13 @@ impl<'s, 'w> Commands<'s, 'w> {
/// }
/// # example_system.system();
/// ```
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'s, 'w, '_> {
#[track_caller]
pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
assert!(
self.entities.contains(entity),
"Attempting to create an EntityCommands for entity {:?}, which doesn't exist.",
entity
);
EntityCommands {
entity,
commands: self,
@ -155,6 +167,23 @@ impl<'s, 'w> Commands<'s, 'w> {
self.queue.push(SpawnBatch { bundles_iter });
}
/// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given
/// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists).
/// This is faster than doing equivalent operations one-by-one.
///
/// # Note
/// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`Commands::spawn_batch`].
/// This method should generally only be used for sharing entities across apps, and only when they have a scheme
/// worked out to share an ID space (which doesn't happen by default).
pub fn insert_or_spawn_batch<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`].
pub fn insert_resource<T: Component>(&mut self, resource: T) {
self.queue.push(InsertResource { resource })
@ -174,12 +203,12 @@ impl<'s, 'w> Commands<'s, 'w> {
}
/// A list of commands that will be run to modify an [`Entity`].
pub struct EntityCommands<'s, 'w, 'a> {
pub struct EntityCommands<'w, 's, 'a> {
entity: Entity,
commands: &'a mut Commands<'s, 'w>,
commands: &'a mut Commands<'w, 's>,
}
impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// Retrieves the current entity's unique [`Entity`] id.
#[inline]
pub fn id(&self) -> Entity {
@ -267,7 +296,7 @@ impl<'s, 'w, 'a> EntityCommands<'s, 'w, 'a> {
}
/// Returns the underlying `[Commands]`.
pub fn commands(&mut self) -> &mut Commands<'s, 'w> {
pub fn commands(&mut self) -> &mut Commands<'w, 's> {
self.commands
}
}
@ -314,6 +343,32 @@ where
}
}
pub struct InsertOrSpawnBatch<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)]
pub struct Despawn {
pub entity: Entity,
@ -322,7 +377,9 @@ pub struct Despawn {
impl Command for Despawn {
fn write(self, world: &mut World) {
if !world.despawn(self.entity) {
debug!("Failed to despawn non-existent entity {:?}", self.entity);
warn!("Could not despawn entity {:?} because it doesn't exist in this World.\n\
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", self.entity);
}
}
}
@ -337,7 +394,13 @@ where
T: Bundle + 'static,
{
fn write(self, world: &mut World) {
world.entity_mut(self.entity).insert_bundle(self.bundle);
if let Some(mut entity) = world.get_entity_mut(self.entity) {
entity.insert_bundle(self.bundle);
} else {
panic!("Could not insert a bundle (of type `{}`) for entity {:?} because it doesn't exist in this World.\n\
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
}
}
}
@ -352,7 +415,13 @@ where
T: Component,
{
fn write(self, world: &mut World) {
world.entity_mut(self.entity).insert(self.component);
if let Some(mut entity) = world.get_entity_mut(self.entity) {
entity.insert(self.component);
} else {
panic!("Could not add a component (of type `{}`) to entity {:?} because it doesn't exist in this World.\n\
If this command was added to a newly spawned entity, ensure that you have not despawned that entity within the same stage.\n\
This may have occurred due to system order ambiguity, or if the spawning system has multiple command buffers", std::any::type_name::<T>(), self.entity);
}
}
}

View file

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

View file

@ -24,7 +24,7 @@ pub struct SystemMeta {
}
impl SystemMeta {
pub fn new<T>() -> Self {
fn new<T>() -> Self {
Self {
name: std::any::type_name::<T>().into(),
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.
#[inline]
pub fn get<'s, 'w>(
pub fn get<'w, 's>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item
where
Param::Fetch: ReadOnlySystemParamFetch,
{
@ -101,10 +101,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the mutable [`SystemParam`] values.
#[inline]
pub fn get_mut<'s, 'w>(
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
self.validate_world_and_update_archetypes(world);
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
unsafe { self.get_unchecked_manual(world) }
@ -145,10 +145,10 @@ impl<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
/// created with.
#[inline]
pub unsafe fn get_unchecked_manual<'s, 'w>(
pub unsafe fn get_unchecked_manual<'w, 's>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'s, 'w>>::Item {
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item {
let change_tick = world.increment_change_tick();
let param = <Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,
@ -228,7 +228,7 @@ pub struct InputMarker;
///
/// You get this by calling [`IntoSystem::system`] on a function that only accepts
/// [`SystemParam`]s. The output of the system becomes the functions return type, while the input
/// becomes the functions [`In`] tagged parameter or `()` if no such paramater exists.
/// becomes the functions [`In`] tagged parameter or `()` if no such parameter exists.
pub struct FunctionSystem<In, Out, Param, Marker, F>
where
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>) {
/// assert_eq!(*local, 42);
/// }
/// let mut system = local_is_42.system().config(|config| config.0 = Some(42));
/// let mut system = local_is_42.config(|config| config.0 = Some(42));
/// system.initialize(world);
/// system.run((), world);
/// ```

View file

@ -24,11 +24,11 @@ mod tests {
bundle::Bundles,
component::Components,
entity::{Entities, Entity},
query::{Added, Changed, Or, With, Without},
query::{Added, Changed, Or, QueryState, With, Without},
schedule::{Schedule, Stage, SystemStage},
system::{
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet,
RemovedComponents, Res, ResMut, System, SystemState,
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, NonSend, NonSendMut, Query,
QuerySet, RemovedComponents, Res, ResMut, System, SystemState,
},
world::{FromWorld, World},
};
@ -60,7 +60,7 @@ mod tests {
system.run((), &mut world);
}
fn run_system<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 update = SystemStage::parallel();
update.add_system(system);
@ -121,7 +121,7 @@ mod tests {
world.spawn().insert_bundle((A, C));
world.spawn().insert_bundle((A, D));
run_system(&mut world, query_system.system());
run_system(&mut world, query_system);
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
}
@ -131,9 +131,9 @@ mod tests {
// Regression test for issue #762
fn query_system(
mut ran: ResMut<bool>,
set: QuerySet<(
Query<(), Or<(Changed<A>, Changed<B>)>>,
Query<(), Or<(Added<A>, Added<B>)>>,
mut set: QuerySet<(
QueryState<(), Or<(Changed<A>, Changed<B>)>>,
QueryState<(), Or<(Added<A>, Added<B>)>>,
)>,
) {
let changed = set.q0().iter().count();
@ -149,7 +149,7 @@ mod tests {
world.insert_resource(false);
world.spawn().insert_bundle((A, B));
run_system(&mut world, query_system.system());
run_system(&mut world, query_system);
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
}
@ -179,7 +179,7 @@ mod tests {
let mut schedule = Schedule::default();
let mut update = SystemStage::parallel();
update.add_system(incr_e_on_flip.system());
update.add_system(incr_e_on_flip);
schedule.add_stage("update", update);
schedule.add_stage(
"clear_trackers",
@ -206,7 +206,7 @@ mod tests {
fn sys(_q1: Query<&mut A>, _q2: Query<&mut A>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
@ -214,7 +214,7 @@ mod tests {
fn sys(_q1: Query<&mut A, With<B>>, _q2: Query<&mut A, Without<B>>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
@ -222,7 +222,7 @@ mod tests {
fn sys(_q1: Query<(&mut A, &B)>, _q2: Query<&mut A, Without<B>>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
@ -231,32 +231,36 @@ mod tests {
fn sys(_q1: Query<&A>, _q2: Query<&mut A>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
fn query_set_system() {
fn sys(mut _set: QuerySet<(Query<&mut A>, Query<&A>)>) {}
fn sys(mut _set: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
#[should_panic]
fn conflicting_query_with_query_set_system() {
fn sys(_query: Query<&mut A>, _set: QuerySet<(Query<&mut A>, Query<&B>)>) {}
fn sys(_query: Query<&mut A>, _set: QuerySet<(QueryState<&mut A>, QueryState<&B>)>) {}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
#[should_panic]
fn conflicting_query_sets_system() {
fn sys(_set_1: QuerySet<(Query<&mut A>,)>, _set_2: QuerySet<(Query<&mut A>, Query<&B>)>) {}
fn sys(
_set_1: QuerySet<(QueryState<&mut A>,)>,
_set_2: QuerySet<(QueryState<&mut A>, QueryState<&B>)>,
) {
}
let mut world = World::default();
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[derive(Default)]
@ -264,39 +268,39 @@ mod tests {
_buffer: Vec<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();
world.insert_resource(BufferRes::default());
world.insert_resource(A);
world.insert_resource(B);
run_system(&mut world, sys.system());
run_system(&mut world, sys);
}
#[test]
#[should_panic]
fn conflicting_system_resources() {
fn sys(_: ResMut<BufferRes>, _: Res<BufferRes>) {}
test_for_conflicting_resources(sys.system())
test_for_conflicting_resources(sys)
}
#[test]
#[should_panic]
fn conflicting_system_resources_reverse_order() {
fn sys(_: Res<BufferRes>, _: ResMut<BufferRes>) {}
test_for_conflicting_resources(sys.system())
test_for_conflicting_resources(sys)
}
#[test]
#[should_panic]
fn conflicting_system_resources_multiple_mutable() {
fn sys(_: ResMut<BufferRes>, _: ResMut<BufferRes>) {}
test_for_conflicting_resources(sys.system())
test_for_conflicting_resources(sys)
}
#[test]
fn nonconflicting_system_resources() {
fn sys(_: Local<BufferRes>, _: ResMut<BufferRes>, _: Local<A>, _: ResMut<A>) {}
test_for_conflicting_resources(sys.system())
test_for_conflicting_resources(sys)
}
#[test]
@ -321,12 +325,54 @@ mod tests {
*modified = true;
}
run_system(&mut world, sys.system());
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.get_resource::<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]
fn remove_tracking() {
let mut world = World::new();
@ -352,7 +398,7 @@ mod tests {
*ran = true;
}
run_system(&mut world, validate_removed.system());
run_system(&mut world, validate_removed);
assert!(*world.get_resource::<bool>().unwrap(), "system ran");
}
@ -365,18 +411,10 @@ mod tests {
*modified = true;
}
run_system(
&mut world,
sys.system().config(|config| config.0 = Some(42)),
);
run_system(&mut world, sys.config(|config| config.0 = Some(42)));
// ensure the system actually ran
assert!(*world.get_resource::<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]
@ -417,7 +455,7 @@ mod tests {
*modified = true;
}
run_system(&mut world, sys.system());
run_system(&mut world, sys);
// ensure the system actually ran
assert!(*world.get_resource::<bool>().unwrap());
@ -528,12 +566,15 @@ mod tests {
world.insert_resource(A(42));
world.spawn().insert(B(7));
let mut system_state: SystemState<(Res<A>, Query<&B>, QuerySet<(Query<&C>, Query<&D>)>)> =
SystemState::new(&mut world);
let mut system_state: SystemState<(
Res<A>,
Query<&B>,
QuerySet<(QueryState<&C>, QueryState<&D>)>,
)> = SystemState::new(&mut world);
let (a, query, _) = system_state.get(&world);
assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!(
*query.single().unwrap(),
*query.single(),
B(7),
"returned component matches initial value"
);
@ -560,7 +601,7 @@ mod tests {
let (a, mut query) = system_state.get_mut(&mut world);
assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!(
*query.single_mut().unwrap(),
*query.single_mut(),
B(7),
"returned component matches initial value"
);
@ -577,18 +618,18 @@ mod tests {
let mut system_state: SystemState<Query<&A, Changed<A>>> = SystemState::new(&mut world);
{
let query = system_state.get(&world);
assert_eq!(*query.single().unwrap(), A(1));
assert_eq!(*query.single(), A(1));
}
{
let query = system_state.get(&world);
assert!(query.single().is_err());
assert!(query.get_single().is_err());
}
world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
{
let query = system_state.get(&world);
assert_eq!(*query.single().unwrap(), A(2));
assert_eq!(*query.single(), A(2));
}
}
@ -632,4 +673,199 @@ mod tests {
);
}
}
/// this test exists to show that read-only world-only queries can return data that lives as long as 'world
#[test]
#[allow(unused)]
fn long_life_test() {
struct Holder<'w> {
value: &'w A,
}
struct State {
state: SystemState<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)
/// bevy has to offer.
pub struct Query<'w, 's, Q: WorldQuery, F: WorldQuery = ()>
pub struct Query<'world, 'state, Q: WorldQuery, F: WorldQuery = ()>
where
F::Fetch: FilterFetch,
{
pub(crate) world: &'w World,
pub(crate) state: &'s QueryState<Q, F>,
pub(crate) world: &'world World,
pub(crate) state: &'state QueryState<Q, F>,
pub(crate) last_change_tick: u32,
pub(crate) change_tick: u32,
}
@ -146,7 +146,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
#[inline]
pub fn iter(&self) -> QueryIter<'w, 's, Q, F>
pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F>
where
Q::Fetch: ReadOnlyFetch,
{
@ -158,6 +158,17 @@ where
}
}
/// Returns an [`Iterator`] over the query results.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
}
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
/// This can only be called for read-only queries
///
@ -166,7 +177,7 @@ where
/// - if K < N: all possible K-sized combinations of query results, without repetition
/// - if K > N: empty set (no K-sized combinations exist)
#[inline]
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'w, 's, Q, F, K>
pub fn iter_combinations<const K: usize>(&self) -> QueryCombinationIter<'_, '_, Q, F, K>
where
Q::Fetch: ReadOnlyFetch,
{
@ -181,17 +192,6 @@ where
}
}
/// Returns an [`Iterator`] over the query results.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'w, 's, Q, F> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
}
}
/// Iterates over all possible combinations of `K` query results without repetition.
///
/// The returned value is not an `Iterator`, because that would lead to aliasing of mutable references.
@ -216,7 +216,7 @@ where
#[inline]
pub fn iter_combinations_mut<const K: usize>(
&mut self,
) -> QueryCombinationIter<'w, 's, Q, F, K> {
) -> QueryCombinationIter<'_, '_, Q, F, K> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -235,7 +235,7 @@ where
/// This function makes it possible to violate Rust's aliasing guarantees. You must make sure
/// this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn iter_unsafe(&self) -> QueryIter<'w, 's, Q, F> {
pub unsafe fn iter_unsafe(&self) -> QueryIter<'_, '_, Q, F> {
// SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state
@ -251,7 +251,7 @@ where
#[inline]
pub unsafe fn iter_combinations_unsafe<const K: usize>(
&self,
) -> QueryCombinationIter<'w, 's, Q, F, K> {
) -> QueryCombinationIter<'_, '_, Q, F, K> {
// SEMI-SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
self.state.iter_combinations_unchecked_manual(
@ -266,7 +266,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
#[inline]
pub fn for_each(&self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
pub fn for_each(&'s self, f: impl FnMut(<Q::Fetch as Fetch<'w, 's>>::Item))
where
Q::Fetch: ReadOnlyFetch,
{
@ -285,7 +285,7 @@ where
/// Runs `f` on each query result. This is faster than the equivalent iter() method, but cannot
/// be chained like a normal [`Iterator`].
#[inline]
pub fn for_each_mut(&mut self, f: impl FnMut(<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
// borrow checks when they conflict
unsafe {
@ -304,7 +304,7 @@ where
/// write-queries.
#[inline]
pub fn par_for_each(
&self,
&'s self,
task_pool: &TaskPool,
batch_size: usize,
f: impl Fn(<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.
#[inline]
pub fn par_for_each_mut(
&mut self,
pub fn par_for_each_mut<'a>(
&'a mut self,
task_pool: &TaskPool,
batch_size: usize,
f: impl Fn(<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
// borrow checks when they conflict
@ -351,7 +351,10 @@ where
///
/// This can only be called for read-only queries, see [`Self::get_mut`] for write-queries.
#[inline]
pub fn get(&self, entity: Entity) -> Result<<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
Q::Fetch: ReadOnlyFetch,
{
@ -372,7 +375,7 @@ where
pub fn get_mut(
&mut self,
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.
// same-system queries have runtime borrow checks when they conflict
unsafe {
@ -393,7 +396,7 @@ where
/// this call does not result in multiple mutable references to the same component
#[inline]
pub unsafe fn get_unchecked(
&'s self,
&self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch>::Item, QueryEntityError> {
// SEMI-SAFE: system runs without conflicts with other systems.
@ -442,7 +445,7 @@ where
pub fn get_component_mut<T: Component>(
&mut self,
entity: Entity,
) -> Result<Mut<'w, T>, QueryComponentError> {
) -> Result<Mut<'_, T>, QueryComponentError> {
// SAFE: unique access to query (preventing aliased access)
unsafe { self.get_component_unchecked_mut(entity) }
}
@ -459,7 +462,7 @@ where
pub unsafe fn get_component_unchecked_mut<T: Component>(
&self,
entity: Entity,
) -> Result<Mut<'w, T>, QueryComponentError> {
) -> Result<Mut<'_, T>, QueryComponentError> {
let world = self.world;
let entity_ref = world
.get_entity(entity)
@ -485,6 +488,33 @@ where
}
}
/// Gets the result of a single-result query.
///
/// Assumes this query has only one result and panics if there are no or multiple results.
/// Use [`Self::get_single`] to handle the error cases explicitly
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::{IntoSystem, Query, With};
/// struct Player;
/// struct Position(f32, f32);
/// fn player_system(query: Query<&Position, With<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.
///
/// If the query has exactly one result, returns the result inside `Ok`
@ -494,27 +524,28 @@ where
/// # Examples
///
/// ```
/// # use bevy_ecs::prelude::{IntoSystem, With};
/// # use bevy_ecs::system::{Query, QuerySingleError};
/// # use bevy_ecs::prelude::IntoSystem;
/// struct PlayerScore(i32);
/// fn player_scoring_system(query: Query<&PlayerScore>) {
/// match query.single() {
/// Ok(PlayerScore(score)) => {
/// // do something with score
/// struct Player;
/// struct Position(f32, f32);
/// fn player_system(query: Query<&Position, With<Player>>) {
/// match query.get_single() {
/// Ok(position) => {
/// // do something with position
/// }
/// Err(QuerySingleError::NoEntities(_)) => {
/// // no PlayerScore
/// // no position with Player
/// }
/// Err(QuerySingleError::MultipleEntities(_)) => {
/// // multiple PlayerScore
/// // multiple position with Player
/// }
/// }
/// }
/// # let _check_that_its_a_system = player_scoring_system.system();
/// # let _check_that_its_a_system = player_system.system();
/// ```
///
/// This can only be called for read-only queries, see [`Self::single_mut`] for write-queries.
pub fn single(&self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
/// This can only be called for read-only queries, see [`Self::get_single_mut`] for write-queries.
pub fn get_single(&'s self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -531,9 +562,18 @@ where
}
}
/// Gets the query result if it is only a single result, otherwise panics
/// If you want to handle the error case yourself you can use the [`Self::get_single_mut`] variant.
#[track_caller]
pub fn single_mut(&mut self) -> <Q::Fetch as Fetch<'_, '_>>::Item {
self.get_single_mut().unwrap()
}
/// Gets the query result if it is only a single result, otherwise returns a
/// [`QuerySingleError`].
pub fn single_mut(&mut self) -> Result<<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 first = query.next();
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 added to an application using `AppBuilder::add_system(my_system.system())`
/// Systems are added to an application using `App::add_system(my_system)`
/// or similar methods, and will generally run once per pass of the main loop.
///
/// Systems are executed in parallel, in opportunistic order; data access is managed automatically.

View file

@ -27,13 +27,12 @@ use std::{
///
/// ```
/// # use bevy_ecs::prelude::*;
/// # use std::marker::PhantomData;
/// use std::marker::PhantomData;
/// use bevy_ecs::system::SystemParam;
///
/// #[derive(SystemParam)]
/// struct MyParam<'s, 'w> {
/// struct MyParam<'w, 's> {
/// foo: Res<'w, usize>,
/// // TODO: this isn't ideal ... maybe the SystemParam derive can be smarter about world and state lifetimes?
/// #[system_param(ignore)]
/// marker: PhantomData<&'s usize>,
/// }
@ -45,7 +44,7 @@ use std::{
/// # my_system.system();
/// ```
pub trait SystemParam: Sized {
type Fetch: for<'s, 'w> SystemParamFetch<'s, 'w>;
type Fetch: for<'w, 's> SystemParamFetch<'w, 's>;
}
/// The state of a [`SystemParam`].
@ -84,21 +83,21 @@ pub unsafe trait SystemParamState: Send + Sync + 'static {
/// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`]
pub unsafe trait ReadOnlySystemParamFetch {}
pub trait SystemParamFetch<'s, 'w>: SystemParamState {
pub trait SystemParamFetch<'world, 'state>: SystemParamState {
type Item;
/// # Safety
///
/// This call might access any of the input parameters in an unsafe way. Make sure the data
/// access is safe in the context of the system scheduler.
unsafe fn get_param(
state: &'s mut Self,
state: &'state mut Self,
system_meta: &SystemMeta,
world: &'w World,
world: &'world World,
change_tick: u32,
) -> Self::Item;
}
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -150,7 +149,7 @@ where
fn default_config() {}
}
impl<'s, 'w, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'s, 'w>
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's>
for QueryState<Q, F>
where
F::Fetch: FilterFetch,
@ -189,7 +188,13 @@ fn assert_component_access_compatibility(
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);
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> {
component_id: ComponentId,
marker: PhantomData<T>,
@ -295,7 +300,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
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>;
#[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>);
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() {}
}
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>>;
#[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> {
component_id: ComponentId,
marker: PhantomData<T>,
@ -409,7 +415,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
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>;
#[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>);
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() {}
}
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>>;
#[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;
}
@ -501,8 +508,8 @@ unsafe impl SystemParamState for CommandQueue {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for CommandQueue {
type Item = Commands<'s, 'w>;
impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
type Item = Commands<'w, 's>;
#[inline]
unsafe fn get_param(
@ -571,7 +578,7 @@ impl<'a, T: Component> DerefMut for Local<'a, T> {
}
}
/// The [`SystemParamState`] of [`Local`].
/// The [`SystemParamState`] of [`Local<T>`].
pub struct LocalState<T: Component>(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>;
#[inline]
@ -639,7 +646,7 @@ impl<'a, T> RemovedComponents<'a, T> {
// SAFE: Only reads World components
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
/// The [`SystemParamState`] of [`RemovedComponents`].
/// The [`SystemParamState`] of [`RemovedComponents<T>`].
pub struct RemovedComponentsState<T> {
component_id: ComponentId,
marker: PhantomData<T>,
@ -664,7 +671,7 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
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>;
#[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> {
component_id: ComponentId,
marker: PhantomData<fn() -> T>,
@ -779,7 +786,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
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>;
#[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>);
impl<'a, T: Component> SystemParam for Option<NonSend<'a, T>> {
impl<'w, T: 'static> SystemParam for Option<NonSend<'w, T>> {
type Fetch = OptionNonSendState<T>;
}
@ -829,7 +837,7 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
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>>;
#[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> {
component_id: ComponentId,
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 {
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();
if combined_access.has_write(component_id) {
panic!(
@ -898,7 +906,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
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>;
#[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>);
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() {}
}
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>>;
#[inline]
@ -991,7 +1000,7 @@ unsafe impl SystemParamState for ArchetypesState {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for ArchetypesState {
impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState {
type Item = &'w Archetypes;
#[inline]
@ -1026,7 +1035,7 @@ unsafe impl SystemParamState for ComponentsState {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for ComponentsState {
impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState {
type Item = &'w Components;
#[inline]
@ -1061,7 +1070,7 @@ unsafe impl SystemParamState for EntitiesState {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for EntitiesState {
impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState {
type Item = &'w Entities;
#[inline]
@ -1096,7 +1105,7 @@ unsafe impl SystemParamState for BundlesState {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for BundlesState {
impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState {
type Item = &'w Bundles;
#[inline]
@ -1123,7 +1132,7 @@ impl SystemParam for SystemChangeTick {
type Fetch = SystemChangeTickState;
}
/// The [`SystemParamState`] of [`SystemChangeTickState`].
/// The [`SystemParamState`] of [`SystemChangeTick`].
pub struct SystemChangeTickState {}
unsafe impl SystemParamState for SystemChangeTickState {
@ -1136,7 +1145,7 @@ unsafe impl SystemParamState for SystemChangeTickState {
fn default_config() {}
}
impl<'s, 'w> SystemParamFetch<'s, 'w> for SystemChangeTickState {
impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState {
type Item = SystemChangeTick;
unsafe fn get_param(
@ -1163,7 +1172,7 @@ macro_rules! impl_system_param_tuple {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'s, 'w, $($param: SystemParamFetch<'s, 'w>),*> SystemParamFetch<'s, 'w> for ($($param,)*) {
impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) {
type Item = ($($param::Item,)*);
#[inline]

View file

@ -1,5 +1,5 @@
use crate::{
archetype::{Archetype, ArchetypeId, Archetypes, ComponentStatus},
archetype::{Archetype, ArchetypeId, Archetypes},
bundle::{Bundle, BundleInfo},
change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components, StorageType},
@ -189,112 +189,29 @@ impl<'w> EntityMut<'w> {
})
}
/// # Safety:
/// Partially moves the entity to a new archetype based on the provided bundle info
/// You must handle the other part of moving the entity yourself
unsafe fn get_insert_bundle_info<'a>(
entities: &mut Entities,
archetypes: &'a mut Archetypes,
components: &mut Components,
storages: &mut Storages,
bundle_info: &BundleInfo,
current_location: EntityLocation,
entity: Entity,
) -> (&'a Archetype, &'a Vec<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 {
let change_tick = self.world.change_tick();
let bundle_info = self
.world
.bundles
.init_info::<T>(&mut self.world.components);
let (archetype, bundle_status, new_location) = unsafe {
Self::get_insert_bundle_info(
&mut self.world.entities,
&mut self.world.archetypes,
&mut self.world.components,
&mut self.world.storages,
bundle_info,
self.location,
self.entity,
)
};
self.location = new_location;
let table = &mut self.world.storages.tables[archetype.table_id()];
let table_row = archetype.entity_table_row(new_location.index);
// SAFE: table row is valid
let mut bundle_inserter = bundle_info.get_bundle_inserter(
&mut self.world.entities,
&mut self.world.archetypes,
&mut self.world.components,
&mut self.world.storages,
self.location.archetype_id,
change_tick,
);
// SAFE: location matches current entity. `T` matches `bundle_info`
unsafe {
bundle_info.write_components(
&mut self.world.storages.sparse_sets,
self.entity,
table,
table_row,
bundle_status,
bundle,
change_tick,
)
};
self.location = bundle_inserter.insert(self.entity, self.location.index, bundle);
}
self
}
// TODO: move to BundleInfo
pub fn remove_bundle<T: Bundle>(&mut self) -> Option<T> {
let archetypes = &mut self.world.archetypes;
let storages = &mut self.world.storages;
@ -340,6 +257,42 @@ impl<'w> EntityMut<'w> {
})
};
unsafe {
Self::move_entity_from_remove::<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);
if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location;
@ -349,36 +302,37 @@ impl<'w> EntityMut<'w> {
let new_archetype = &mut archetypes[new_archetype_id];
let new_location = if old_table_id == new_archetype.table_id() {
unsafe { new_archetype.allocate(entity, old_table_row) }
new_archetype.allocate(entity, old_table_row)
} else {
let (old_table, new_table) = storages
.tables
.get_2_mut(old_table_id, new_archetype.table_id());
// SAFE: table_row exists. All "missing" components have been extracted into the bundle
// above and the caller takes ownership
let move_result =
unsafe { old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table) };
// SAFE: old_table_row exists
let move_result = if DROP {
old_table.move_to_and_drop_missing_unchecked(old_table_row, new_table)
} else {
old_table.move_to_and_forget_missing_unchecked(old_table_row, new_table)
};
// SAFE: new_table_row is a valid position in new_archetype's table
let new_location = unsafe { new_archetype.allocate(entity, move_result.new_row) };
// SAFE: move_result.new_row is a valid position in new_archetype's table
let new_location = new_archetype.allocate(entity, move_result.new_row);
// if an entity was moved into this entity's table spot, update its table row
if let Some(swapped_entity) = move_result.swapped_entity {
let swapped_location = entities.get(swapped_entity).unwrap();
let archetype = &mut archetypes[swapped_location.archetype_id];
archetype.set_entity_table_row(swapped_location.index, old_table_row);
archetypes[swapped_location.archetype_id]
.set_entity_table_row(swapped_location.index, old_table_row);
}
new_location
};
self.location = new_location;
entities.meta[self.entity.id as usize].location = new_location;
Some(result)
*self_location = new_location;
entities.meta[entity.id as usize].location = new_location;
}
// TODO: move to BundleInfo
/// Remove any components in the bundle that the entity has.
pub fn remove_bundle_intersection<T: Bundle>(&mut self) {
let archetypes = &mut self.world.archetypes;
@ -425,40 +379,18 @@ impl<'w> EntityMut<'w> {
}
}
let remove_result = old_archetype.swap_remove(old_location.index);
if let Some(swapped_entity) = remove_result.swapped_entity {
entities.meta[swapped_entity.id as usize].location = old_location;
unsafe {
Self::move_entity_from_remove::<true>(
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 {
@ -531,6 +463,7 @@ impl<'w> EntityMut<'w> {
}
}
// TODO: move to Storages?
/// # Safety
/// `entity_location` must be within bounds of the given archetype and `entity` must exist inside
/// the archetype
@ -560,6 +493,7 @@ unsafe fn get_component(
}
}
// TODO: move to Storages?
/// # Safety
/// Caller must ensure that `component_id` is valid
#[inline]
@ -590,6 +524,7 @@ unsafe fn get_component_and_ticks(
}
}
// TODO: move to Storages?
/// Moves component data out of storage.
///
/// This function leaves the underlying memory unchanged, but the component behind
@ -671,95 +606,6 @@ fn contains_component_with_id(
world.archetypes[location.archetype_id].contains(component_id)
}
/// Adds a bundle to the given archetype and returns the resulting archetype. This could be the same
/// [ArchetypeId], in the event that adding the given bundle does not result in an Archetype change.
/// Results are cached in the Archetype Graph to avoid redundant work.
///
/// # Safety
/// components in `bundle_info` must exist
pub(crate) unsafe fn add_bundle_to_archetype(
archetypes: &mut Archetypes,
storages: &mut Storages,
components: &mut Components,
archetype_id: ArchetypeId,
bundle_info: &BundleInfo,
) -> ArchetypeId {
if let Some(add_bundle) = archetypes[archetype_id]
.edges()
.get_add_bundle(bundle_info.id)
{
return add_bundle.archetype_id;
}
let mut new_table_components = Vec::new();
let mut new_sparse_set_components = Vec::new();
let mut bundle_status = Vec::with_capacity(bundle_info.component_ids.len());
let current_archetype = &mut archetypes[archetype_id];
for component_id in bundle_info.component_ids.iter().cloned() {
if current_archetype.contains(component_id) {
bundle_status.push(ComponentStatus::Mutated);
} else {
bundle_status.push(ComponentStatus::Added);
let component_info = components.get_info_unchecked(component_id);
match component_info.storage_type() {
StorageType::Table => new_table_components.push(component_id),
StorageType::SparseSet => {
storages.sparse_sets.get_or_insert(component_info);
new_sparse_set_components.push(component_id)
}
}
}
}
if new_table_components.is_empty() && new_sparse_set_components.is_empty() {
let edges = current_archetype.edges_mut();
// the archetype does not change when we add this bundle
edges.set_add_bundle(bundle_info.id, archetype_id, bundle_status);
archetype_id
} else {
let table_id;
let table_components;
let sparse_set_components;
// the archetype changes when we add this bundle. prepare the new archetype and storages
{
let current_archetype = &archetypes[archetype_id];
table_components = if new_table_components.is_empty() {
// if there are no new table components, we can keep using this table
table_id = current_archetype.table_id();
current_archetype.table_components().to_vec()
} else {
new_table_components.extend(current_archetype.table_components());
// sort to ignore order while hashing
new_table_components.sort();
// SAFE: all component ids in `new_table_components` exist
table_id = storages
.tables
.get_id_or_insert(&new_table_components, components);
new_table_components
};
sparse_set_components = if new_sparse_set_components.is_empty() {
current_archetype.sparse_set_components().to_vec()
} else {
new_sparse_set_components.extend(current_archetype.sparse_set_components());
// sort to ignore order while hashing
new_sparse_set_components.sort();
new_sparse_set_components
};
};
let new_archetype_id =
archetypes.get_id_or_insert(table_id, table_components, sparse_set_components);
// add an edge from the old archetype to the new archetype
archetypes[archetype_id].edges_mut().set_add_bundle(
bundle_info.id,
new_archetype_id,
bundle_status,
);
new_archetype_id
}
}
/// Removes a bundle from the given archetype and returns the resulting archetype (or None if the
/// removal was invalid). in the event that adding the given bundle does not result in an Archetype
/// change. Results are cached in the Archetype Graph to avoid redundant work.
@ -814,7 +660,7 @@ unsafe fn remove_bundle_from_archetype(
// graph
current_archetype
.edges_mut()
.set_remove_bundle(bundle_info.id, None);
.insert_remove_bundle(bundle_info.id, None);
return None;
}
}
@ -853,11 +699,11 @@ unsafe fn remove_bundle_from_archetype(
if intersection {
current_archetype
.edges_mut()
.set_remove_bundle_intersection(bundle_info.id, result);
.insert_remove_bundle_intersection(bundle_info.id, result);
} else {
current_archetype
.edges_mut()
.set_remove_bundle(bundle_info.id, result);
.insert_remove_bundle(bundle_info.id, result);
}
result
}

View file

@ -1,16 +1,15 @@
mod entity_ref;
mod pointer;
mod spawn_batch;
mod world_cell;
pub use crate::change_detection::Mut;
pub use entity_ref::*;
pub use pointer::*;
pub use spawn_batch::*;
pub use world_cell::*;
use crate::{
archetype::{ArchetypeComponentId, ArchetypeComponentInfo, ArchetypeId, Archetypes},
bundle::{Bundle, Bundles},
bundle::{Bundle, BundleInserter, BundleSpawner, Bundles},
change_detection::Ticks,
component::{
Component, ComponentDescriptor, ComponentId, ComponentTicks, Components, ComponentsError,
@ -221,8 +220,13 @@ impl World {
self.get_entity_mut(entity).expect("Entity does not exist")
}
/// Returns an EntityMut for an existing entity or creates one if it doesn't exist.
/// This will return `None` if the entity exists with a different generation.
/// Returns an [EntityMut] for the given `entity` (if it exists) or spawns one if it doesn't exist.
/// This will return [None] if the `entity` exists with a different generation.
///
/// # Note
/// Spawning a specific `entity` value is rarely the right choice. Most apps should favor [`World::spawn`].
/// This method should generally only be used for sharing entities across apps, and only when they have a
/// scheme worked out to share an ID space (which doesn't happen by default).
#[inline]
pub fn get_or_spawn(&mut self, entity: Entity) -> Option<EntityMut> {
self.flush();
@ -379,6 +383,7 @@ impl World {
/// .id();
/// let position = world.get::<Position>(entity).unwrap();
/// assert_eq!(position.x, 0.0);
/// ```
#[inline]
pub fn get<T: Component>(&self, entity: Entity) -> Option<&T> {
self.get_entity(entity)?.get()
@ -400,6 +405,7 @@ impl World {
/// .id();
/// let mut position = world.get_mut::<Position>(entity).unwrap();
/// position.x = 1.0;
/// ```
#[inline]
pub fn get_mut<T: Component>(&mut self, entity: Entity) -> Option<Mut<T>> {
self.get_entity_mut(entity)?.get_mut()
@ -699,6 +705,127 @@ impl World {
self.get_non_send_unchecked_mut_with_id(component_id)
}
/// For a given batch of ([Entity], [Bundle]) pairs, either spawns each [Entity] with the given
/// bundle (if the entity does not exist), or inserts the [Bundle] (if the entity already exists).
/// This is faster than doing equivalent operations one-by-one.
/// Returns [Ok] if all entities were successfully inserted into or spawned. Otherwise it returns an [Err]
/// with a list of entities that could not be spawned or inserted into. A "spawn or insert" operation can
/// only fail if an [Entity] is passed in with an "invalid generation" that conflicts with an existing [Entity].
///
/// # Note
/// Spawning a specific `entity` value is rarely the right choice. Most apps should use [`World::spawn_batch`].
/// This method should generally only be used for sharing entities across apps, and only when they have a scheme
/// worked out to share an ID space (which doesn't happen by default).
///
/// ```
/// use bevy_ecs::{entity::Entity, world::World};
///
/// let mut world = World::new();
/// let e0 = world.spawn().id();
/// let e1 = world.spawn().id();
/// world.insert_or_spawn_batch(vec![
/// (e0, ("a", 0.0)), // the first entity
/// (e1, ("b", 1.0)), // the second entity
/// ]);
///
/// assert_eq!(world.get::<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
/// returning. This enables safe mutable access to a resource while still providing mutable
/// world access
@ -838,7 +965,7 @@ impl World {
let resource_archetype = self
.archetypes
.archetypes
.get_unchecked_mut(ArchetypeId::resource().index());
.get_unchecked_mut(ArchetypeId::RESOURCE.index());
let resource_archetype_components = &mut resource_archetype.components;
let archetype_component_count = &mut self.archetypes.archetype_component_count;
let components = &self.components;

View file

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

View file

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

View file

@ -296,7 +296,7 @@ mod tests {
.components
.get_resource_id(TypeId::of::<u32>())
.unwrap();
let resource_archetype = world.archetypes.get(ArchetypeId::resource()).unwrap();
let resource_archetype = world.archetypes.get(ArchetypeId::RESOURCE).unwrap();
let u32_archetype_component_id = resource_archetype
.get_archetype_component_id(u32_component_id)
.unwrap();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -151,7 +151,19 @@ fn reflect_binding(
filtering: true,
},
),
_ => panic!("Unsupported bind type {:?}.", binding.descriptor_type),
_ => {
let ReflectDescriptorBinding {
descriptor_type,
name,
set,
binding,
..
} = binding;
panic!(
"Unsupported shader bind type {:?} (name '{}', set {}, binding {})",
descriptor_type, name, set, binding
);
}
};
let shader_stage = match shader_stage {

View file

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

View file

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

View file

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

View file

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

View file

@ -2,10 +2,6 @@
name = "bevy_ui"
version = "0.5.0"
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"
homepage = "https://bevyengine.org"
repository = "https://github.com/bevyengine/bevy"

View file

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

View file

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

View file

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

View file

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

View file

@ -2,7 +2,7 @@ use crate::{CalculatedSize, Node, Style, Val};
use bevy_asset::Assets;
use bevy_ecs::{
entity::Entity,
query::{Changed, Or, With, Without},
query::{Changed, Or, QueryState, With, Without},
system::{Local, Query, QuerySet, Res, ResMut},
};
use bevy_math::Size;
@ -53,9 +53,9 @@ pub fn text_system(
mut font_atlas_set_storage: ResMut<Assets<FontAtlasSet>>,
mut text_pipeline: ResMut<DefaultTextPipeline>,
mut text_queries: QuerySet<(
Query<Entity, Or<(Changed<Text>, Changed<Style>)>>,
Query<Entity, (With<Text>, With<Style>)>,
Query<(&Text, &Style, &mut CalculatedSize)>,
QueryState<Entity, Or<(Changed<Text>, Changed<Style>)>>,
QueryState<Entity, (With<Text>, With<Style>)>,
QueryState<(&Text, &Style, &mut CalculatedSize)>,
)>,
) {
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
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(..) {
if let Ok((text, style, mut calculated_size)) = query.get_mut(entity) {
let node_size = Size::new(

View file

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

View file

@ -9,13 +9,13 @@ fn main() {
fn setup(
mut commands: Commands,
_asset_server: Res<AssetServer>,
// mut materials: ResMut<Assets<ColorMaterial>>,
asset_server: Res<AssetServer>,
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(SpriteBundle {
// material: materials.add(texture_handle.into()),
material: materials.add(texture_handle.into()),
..Default::default()
});
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,9 +11,9 @@ use bevy::{
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(rotator_system.system())
.add_system(camera_order_color_system.system())
.add_startup_system(setup)
.add_system(rotator_system)
.add_system(camera_order_color_system)
.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
`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`
`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.
`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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,7 +29,7 @@ pub struct PrintHelloPlugin;
impl Plugin for PrintHelloPlugin {
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 {
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)))
.add_plugins(DefaultPlugins)
.add_system(system1.system())
.add_system(system1)
.run();
println!("Running another App.");
App::new()
@ -19,7 +19,7 @@ fn main() {
.add_plugins_with(DefaultPlugins, |group| {
group.disable::<bevy::log::LogPlugin>()
})
.add_system(system2.system())
.add_system(system2)
.run();
println!("Done.");
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -272,7 +272,7 @@ fn main() {
// that :) The plugin below runs our app's "system schedule" once every 5 seconds
// (configured above).
.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>()
// Startup systems run exactly once BEFORE all other systems. These are generally used for
// app initialization code (ex: adding entities and resources)

View file

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

View file

@ -12,7 +12,7 @@ fn main() {
App::new()
.add_plugins(DefaultPlugins)
// 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_stage_after(
CoreStage::Update,
@ -24,7 +24,7 @@ fn main() {
// FixedTimestep state from within a system
.with_label(LABEL),
)
.with_system(fixed_update.system()),
.with_system(fixed_update),
)
.run();
}

View file

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

View file

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

View file

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

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