System Param Lifetime Split (#2605)

# Objective

Enable using exact World lifetimes during read-only access . This is motivated by the new renderer's need to allow read-only world-only queries to outlive the query itself (but still be constrained by the world lifetime).

For example:
115b170d1f/pipelined/bevy_pbr2/src/render/mod.rs (L774)

## Solution

Split out SystemParam state and world lifetimes and pipe those lifetimes up to read-only Query ops (and add into_inner for Res). According to every safety test I've run so far (except one), this is safe (see the temporary safety test commit). Note that changing the mutable variants to the new lifetimes would allow aliased mutable pointers (try doing that to see how it affects the temporary safety tests).

The new state lifetime on SystemParam does make `#[derive(SystemParam)]` more cumbersome (the current impl requires PhantomData if you don't use both lifetimes). We can make this better by detecting whether or not a lifetime is used in the derive and adjusting accordingly, but that should probably be done in its own pr.  

## Why is this a draft?

The new lifetimes break QuerySet safety in one very specific case (see the query_set system in system_safety_test). We need to solve this before we can use the lifetimes given.

This is due to the fact that QuerySet is just a wrapper over Query, which now relies on world lifetimes instead of `&self` lifetimes to prevent aliasing (but in systems, each Query has its own implied lifetime, not a centralized world lifetime).  I believe the fix is to rewrite QuerySet to have its own World lifetime (and own the internal reference). This will complicate the impl a bit, but I think it is doable. I'm curious if anyone else has better ideas.

Personally, I think these new lifetimes need to happen. We've gotta have a way to directly tie read-only World queries to the World lifetime. The new renderer is the first place this has come up, but I doubt it will be the last. Worst case scenario we can come up with a second `WorldLifetimeQuery<Q, F = ()>` parameter to enable these read-only scenarios, but I'd rather not add another type to the type zoo.
This commit is contained in:
Carter Anderson 2021-08-15 20:51:53 +00:00
parent a89a954a17
commit 9d453530fa
20 changed files with 481 additions and 256 deletions

View file

@ -9,8 +9,8 @@ use syn::{
parse_macro_input,
punctuated::Punctuated,
token::Comma,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, Lifetime, LitInt,
Path, Result, Token,
Data, DataStruct, DeriveInput, Field, Fields, GenericParam, Ident, Index, LitInt, Path, Result,
Token,
};
struct AllTuples {
@ -176,36 +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 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 fn_name = Ident::new(&format!("q{}", i), Span::call_site());
let fn_name_mut = Ident::new(&format!("q{}_mut", i), Span::call_site());
let index = Index::from(i);
query_fns.push(quote! {
pub fn #fn_name(&self) -> &Query<#lifetime, #query, #filter> {
&self.0.#index
}
});
query_fn_muts.push(quote! {
pub fn #fn_name_mut(&mut self) -> &mut Query<#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,11 +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 query_fn = &query_fns[0..query_count];
let query_fn_mut = &query_fn_muts[0..query_count];
tokens.extend(TokenStream::from(quote! {
impl<#(#lifetime,)* #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParam for QuerySet<(#(Query<#lifetime, #query, #filter>,)*)>
impl<'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>,)*)>;
@ -270,27 +258,30 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
fn default_config() {}
}
impl<'a, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'a> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
impl<'w, 's, #(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamFetch<'w, 's> for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)*
{
type Item = QuerySet<(#(Query<'a, #query, #filter>,)*)>;
type Item = QuerySet<'w, 's, (#(QueryState<#query, #filter>,)*)>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let (#(#query,)*) = &state.0;
QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*))
QuerySet {
query_states: &state.0,
world,
last_change_tick: system_meta.last_change_tick,
change_tick,
}
}
}
impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: WorldQuery,)*> QuerySet<(#(Query<#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)*
}
}));
@ -415,12 +406,12 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
}
}
impl #impl_generics #path::system::SystemParamFetch<'a> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> {
impl #impl_generics #path::system::SystemParamFetch<'w, 's> for #fetch_struct_name <(#(<#field_types as #path::system::SystemParam>::Fetch,)*), #punctuated_generic_idents> {
type Item = #struct_name#ty_generics;
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &#path::system::SystemMeta,
world: &'a #path::world::World,
world: &'w #path::world::World,
change_tick: u32,
) -> Self::Item {
#struct_name {

View file

@ -151,18 +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<'a, T: Component> {
last_event_count: Local<'a, (usize, PhantomData<T>)>,
events: Res<'a, Events<T>>,
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<'a, T: Component> {
events: ResMut<'a, Events<T>>,
pub struct EventWriter<'w, 's, T: Component> {
events: ResMut<'w, Events<T>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'a, T: Component> EventWriter<'a, T> {
impl<'w, 's, T: Component> EventWriter<'w, 's, T> {
pub fn send(&mut self, event: T) {
self.events.send(event);
}
@ -252,7 +254,7 @@ fn internal_event_reader<'a, T>(
}
}
impl<'a, T: Component> EventReader<'a, T> {
impl<'w, 's, T: Component> EventReader<'w, 's, T> {
/// Iterates over the events this EventReader has not seen yet. This updates the EventReader's
/// event counter, which means subsequent event reads will not include events that happened
/// before now.

View file

@ -117,11 +117,11 @@ where
}
#[inline]
pub fn get<'w>(
&mut self,
pub fn get<'w, 's>(
&'s mut self,
world: &'w World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, '_>>::Item, QueryEntityError>
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -130,11 +130,11 @@ where
}
#[inline]
pub fn get_mut<'w>(
&mut self,
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, '_>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
// SAFETY: query has unique world access
unsafe { self.get_unchecked(world, entity) }
}
@ -144,11 +144,11 @@ where
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
#[inline]
pub unsafe fn get_unchecked<'w>(
&mut self,
pub unsafe fn get_unchecked<'w, 's>(
&'s mut self,
world: &'w World,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, '_>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
self.validate_world_and_update_archetypes(world);
self.get_unchecked_manual(
world,
@ -161,13 +161,13 @@ where
/// # Safety
/// This does not check for mutable query correctness. To be safe, make sure mutable queries
/// have unique access to the components they query.
pub unsafe fn get_unchecked_manual<'w>(
&self,
pub unsafe fn get_unchecked_manual<'w, 's>(
&'s self,
world: &'w World,
entity: Entity,
last_change_tick: u32,
change_tick: u32,
) -> Result<<Q::Fetch as Fetch<'w, '_>>::Item, QueryEntityError> {
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError> {
let location = world
.entities
.get(entity)

View file

@ -16,14 +16,14 @@ pub trait Command: Send + Sync + 'static {
}
/// A list of commands that will be run to modify a [`World`].
pub struct Commands<'a> {
queue: &'a mut CommandQueue,
entities: &'a Entities,
pub struct Commands<'w, 's> {
queue: &'s mut CommandQueue,
entities: &'w Entities,
}
impl<'a> Commands<'a> {
impl<'w, 's> Commands<'w, 's> {
/// Create a new `Commands` from a queue and a world.
pub fn new(queue: &'a mut CommandQueue, world: &'a World) -> Self {
pub fn new(queue: &'s mut CommandQueue, world: &'w World) -> Self {
Self {
queue,
entities: world.entities(),
@ -50,7 +50,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn spawn(&mut self) -> EntityCommands<'a, '_> {
pub fn spawn<'a>(&'a mut self) -> EntityCommands<'w, 's, 'a> {
let entity = self.entities.reserve_entity();
EntityCommands {
entity,
@ -98,7 +98,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn spawn_bundle<'b, T: Bundle>(&'b mut self, bundle: T) -> EntityCommands<'a, 'b> {
pub fn spawn_bundle<'a, T: Bundle>(&'a mut self, bundle: T) -> EntityCommands<'w, 's, 'a> {
let mut e = self.spawn();
e.insert_bundle(bundle);
e
@ -123,7 +123,7 @@ impl<'a> Commands<'a> {
/// }
/// # example_system.system();
/// ```
pub fn entity(&mut self, entity: Entity) -> EntityCommands<'a, '_> {
pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
EntityCommands {
entity,
commands: self,
@ -159,12 +159,12 @@ impl<'a> Commands<'a> {
}
/// A list of commands that will be run to modify an [`Entity`].
pub struct EntityCommands<'a, 'b> {
pub struct EntityCommands<'w, 's, 'a> {
entity: Entity,
commands: &'b mut Commands<'a>,
commands: &'a mut Commands<'w, 's>,
}
impl<'a, 'b> EntityCommands<'a, 'b> {
impl<'w, 's, 'a> EntityCommands<'w, 's, 'a> {
/// Retrieves the current entity's unique [`Entity`] id.
#[inline]
pub fn id(&self) -> Entity {
@ -252,7 +252,7 @@ impl<'a, 'b> EntityCommands<'a, 'b> {
}
/// Returns the underlying `[Commands]`.
pub fn commands(&mut self) -> &mut Commands<'a> {
pub fn commands(&mut self) -> &mut Commands<'w, 's> {
self.commands
}
}

View file

@ -87,7 +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<'a>(&'a mut self, world: &'a World) -> <Param::Fetch as SystemParamFetch<'a>>::Item
pub fn get<'w, 's>(
&'s mut self,
world: &'w World,
) -> <Param::Fetch as SystemParamFetch<'w, 's>>::Item
where
Param::Fetch: ReadOnlySystemParamFetch,
{
@ -98,10 +101,10 @@ impl<Param: SystemParam> SystemState<Param> {
/// Retrieve the mutable [`SystemParam`] values.
#[inline]
pub fn get_mut<'a>(
&'a mut self,
world: &'a mut World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
pub fn get_mut<'w, 's>(
&'s mut self,
world: &'w mut World,
) -> <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) }
@ -142,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<'a>(
&'a mut self,
world: &'a World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
pub unsafe fn get_unchecked_manual<'w, 's>(
&'s mut self,
world: &'w World,
) -> <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,

View file

@ -24,7 +24,7 @@ mod tests {
bundle::Bundles,
component::Components,
entity::{Entities, Entity},
query::{Added, Changed, Or, With, Without},
query::{Added, Changed, Or, QueryState, With, Without},
schedule::{Schedule, Stage, SystemStage},
system::{
ConfigurableSystem, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet,
@ -131,9 +131,9 @@ mod tests {
// Regression test for issue #762
fn query_system(
mut ran: ResMut<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();
@ -236,7 +236,7 @@ mod tests {
#[test]
fn query_set_system() {
fn sys(mut _set: QuerySet<(Query<&mut A>, Query<&A>)>) {}
fn sys(mut _set: QuerySet<(QueryState<&mut A>, QueryState<&A>)>) {}
let mut world = World::default();
run_system(&mut world, sys);
}
@ -244,7 +244,7 @@ mod tests {
#[test]
#[should_panic]
fn conflicting_query_with_query_set_system() {
fn sys(_query: Query<&mut A>, _set: QuerySet<(Query<&mut A>, Query<&B>)>) {}
fn sys(_query: Query<&mut A>, _set: QuerySet<(QueryState<&mut A>, QueryState<&B>)>) {}
let mut world = World::default();
run_system(&mut world, sys);
@ -253,7 +253,11 @@ mod tests {
#[test]
#[should_panic]
fn conflicting_query_sets_system() {
fn sys(_set_1: QuerySet<(Query<&mut A>,)>, _set_2: QuerySet<(Query<&mut A>, Query<&B>)>) {}
fn sys(
_set_1: QuerySet<(QueryState<&mut A>,)>,
_set_2: QuerySet<(QueryState<&mut A>, QueryState<&B>)>,
) {
}
let mut world = World::default();
run_system(&mut world, sys);
@ -520,8 +524,11 @@ mod tests {
world.insert_resource(A(42));
world.spawn().insert(B(7));
let mut system_state: SystemState<(Res<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!(
@ -624,4 +631,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

@ -107,17 +107,17 @@ use thiserror::Error;
///
/// This touches all the basics of queries, make sure to check out all the [`WorldQueries`](WorldQuery)
/// bevy has to offer.
pub struct Query<'w, Q: WorldQuery, F: WorldQuery = ()>
pub struct Query<'world, 'state, Q: WorldQuery, F: WorldQuery = ()>
where
F::Fetch: FilterFetch,
{
pub(crate) world: &'w World,
pub(crate) state: &'w QueryState<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,
}
impl<'w, Q: WorldQuery, F: WorldQuery> Query<'w, Q, F>
impl<'w, 's, Q: WorldQuery, F: WorldQuery> Query<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -130,7 +130,7 @@ where
#[inline]
pub(crate) unsafe fn new(
world: &'w World,
state: &'w QueryState<Q, F>,
state: &'s QueryState<Q, F>,
last_change_tick: u32,
change_tick: u32,
) -> Self {
@ -146,7 +146,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::iter_mut`] for write-queries.
#[inline]
pub fn iter(&self) -> QueryIter<'_, '_, Q, F>
pub fn iter(&'s self) -> QueryIter<'w, 's, Q, F>
where
Q::Fetch: ReadOnlyFetch,
{
@ -158,6 +158,17 @@ where
}
}
/// Returns an [`Iterator`] over the query results.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
}
}
/// Returns an [`Iterator`] over all possible combinations of `K` query results without repetition.
/// This can only be called for read-only queries
///
@ -181,17 +192,6 @@ where
}
}
/// Returns an [`Iterator`] over the query results.
#[inline]
pub fn iter_mut(&mut self) -> QueryIter<'_, '_, Q, F> {
// SAFE: system runs without conflicts with other systems.
// same-system queries have runtime borrow checks when they conflict
unsafe {
self.state
.iter_unchecked_manual(self.world, self.last_change_tick, self.change_tick)
}
}
/// Iterates over all possible combinations of `K` query results without repetition.
///
/// The returned value is not an `Iterator`, because that would lead to aliasing of mutable references.
@ -266,7 +266,7 @@ where
///
/// This can only be called for read-only queries, see [`Self::for_each_mut`] for write-queries.
#[inline]
pub fn for_each<'s>(&'s self, f: impl FnMut(<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<'s>(&'s 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 {
@ -303,7 +303,7 @@ where
/// This can only be called for read-only queries, see [`Self::par_for_each_mut`] for
/// write-queries.
#[inline]
pub fn par_for_each<'s>(
pub fn par_for_each(
&'s self,
task_pool: &TaskPool,
batch_size: usize,
@ -327,11 +327,11 @@ where
/// Runs `f` on each query result in parallel using the given task pool.
#[inline]
pub fn par_for_each_mut<'s>(
&'s 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>::Item, QueryEntityError>
pub fn get(
&'s self,
entity: Entity,
) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QueryEntityError>
where
Q::Fetch: ReadOnlyFetch,
{
@ -406,7 +409,10 @@ where
/// entity does not have the given component type or if the given component type does not match
/// this query.
#[inline]
pub fn get_component<T: Component>(&self, entity: Entity) -> Result<&T, QueryComponentError> {
pub fn get_component<T: Component>(
&self,
entity: Entity,
) -> Result<&'w T, QueryComponentError> {
let world = self.world;
let entity_ref = world
.get_entity(entity)
@ -511,7 +517,7 @@ where
/// ```
///
/// 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<'_, '_>>::Item, QuerySingleError>
pub fn single(&'s self) -> Result<<Q::Fetch as Fetch<'w, 's>>::Item, QuerySingleError>
where
Q::Fetch: ReadOnlyFetch,
{

View file

@ -27,11 +27,14 @@ use std::{
///
/// ```
/// # use bevy_ecs::prelude::*;
/// use std::marker::PhantomData;
/// use bevy_ecs::system::SystemParam;
///
/// #[derive(SystemParam)]
/// struct MyParam<'a> {
/// foo: Res<'a, usize>,
/// struct MyParam<'w, 's> {
/// foo: Res<'w, usize>,
/// #[system_param(ignore)]
/// marker: PhantomData<&'s usize>,
/// }
///
/// fn my_system(param: MyParam) {
@ -41,7 +44,7 @@ use std::{
/// # my_system.system();
/// ```
pub trait SystemParam: Sized {
type Fetch: for<'a> SystemParamFetch<'a>;
type Fetch: for<'w, 's> SystemParamFetch<'w, 's>;
}
/// The state of a [`SystemParam`].
@ -80,21 +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<'a>: SystemParamState {
pub trait SystemParamFetch<'world, 'state>: SystemParamState {
type Item;
/// # Safety
///
/// This call might access any of the input parameters in an unsafe way. Make sure the data
/// access is safe in the context of the system scheduler.
unsafe fn get_param(
state: &'a mut Self,
state: &'state mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'world World,
change_tick: u32,
) -> Self::Item;
}
impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'a, Q, F>
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'w, 's, Q, F>
where
F::Fetch: FilterFetch,
{
@ -146,17 +149,18 @@ where
fn default_config() {}
}
impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'a> for QueryState<Q, F>
impl<'w, 's, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamFetch<'w, 's>
for QueryState<Q, F>
where
F::Fetch: FilterFetch,
{
type Item = Query<'a, Q, F>;
type Item = Query<'w, 's, Q, F>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
Query::new(world, state, system_meta.last_change_tick, change_tick)
@ -184,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!();
@ -228,6 +238,10 @@ impl<'w, T: Component> Res<'w, T> {
self.ticks
.is_changed(self.last_change_tick, self.change_tick)
}
pub fn into_inner(self) -> &'w T {
self.value
}
}
impl<'w, T: Component> Deref for Res<'w, T> {
@ -286,14 +300,14 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for ResState<T> {
type Item = Res<'a, T>;
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResState<T> {
type Item = Res<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let column = world
@ -334,14 +348,14 @@ unsafe impl<T: Component> SystemParamState for OptionResState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for OptionResState<T> {
type Item = Option<Res<'a, T>>;
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResState<T> {
type Item = Option<Res<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world
@ -400,14 +414,14 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for ResMutState<T> {
type Item = ResMut<'a, T>;
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for ResMutState<T> {
type Item = ResMut<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
let value = world
@ -447,14 +461,14 @@ unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
type Item = Option<ResMut<'a, T>>;
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for OptionResMutState<T> {
type Item = Option<ResMut<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world
@ -470,7 +484,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
}
}
impl<'a> SystemParam for Commands<'a> {
impl<'w, 's> SystemParam for Commands<'w, 's> {
type Fetch = CommandQueue;
}
@ -492,14 +506,14 @@ unsafe impl SystemParamState for CommandQueue {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for CommandQueue {
type Item = Commands<'a>;
impl<'w, 's> SystemParamFetch<'w, 's> for CommandQueue {
type Item = Commands<'w, 's>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
Commands::new(state, world)
@ -582,14 +596,14 @@ unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
}
}
impl<'a, T: Component + FromWorld> SystemParamFetch<'a> for LocalState<T> {
type Item = Local<'a, T>;
impl<'w, 's, T: Component + FromWorld> SystemParamFetch<'w, 's> for LocalState<T> {
type Item = Local<'s, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
_world: &'a World,
_world: &'w World,
_change_tick: u32,
) -> Self::Item {
Local(&mut state.0)
@ -655,14 +669,14 @@ unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
fn default_config() {}
}
impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState<T> {
type Item = RemovedComponents<'a, T>;
impl<'w, 's, T: Component> SystemParamFetch<'w, 's> for RemovedComponentsState<T> {
type Item = RemovedComponents<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
RemovedComponents {
@ -770,14 +784,14 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
type Item = NonSend<'a, T>;
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendState<T> {
type Item = NonSend<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -803,7 +817,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
/// The [`SystemParamState`] of `Option<NonSend<T>>`.
pub struct OptionNonSendState<T>(NonSendState<T>);
impl<'a, T: Component> SystemParam for Option<NonSend<'a, T>> {
impl<'w, T: Component> SystemParam for Option<NonSend<'w, T>> {
type Fetch = OptionNonSendState<T>;
}
@ -820,14 +834,14 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for OptionNonSendState<T> {
type Item = Option<NonSend<'a, T>>;
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendState<T> {
type Item = Option<NonSend<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -889,14 +903,14 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState<T> {
type Item = NonSendMut<'a, T>;
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for NonSendMutState<T> {
type Item = NonSendMut<'w, T>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -937,14 +951,14 @@ unsafe impl<T: 'static> SystemParamState for OptionNonSendMutState<T> {
fn default_config() {}
}
impl<'a, T: 'static> SystemParamFetch<'a> for OptionNonSendMutState<T> {
type Item = Option<NonSendMut<'a, T>>;
impl<'w, 's, T: 'static> SystemParamFetch<'w, 's> for OptionNonSendMutState<T> {
type Item = Option<NonSendMut<'w, T>>;
#[inline]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {
world.validate_non_send_access::<T>();
@ -982,14 +996,14 @@ unsafe impl SystemParamState for ArchetypesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for ArchetypesState {
type Item = &'a Archetypes;
impl<'w, 's> SystemParamFetch<'w, 's> for ArchetypesState {
type Item = &'w Archetypes;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.archetypes()
@ -1017,14 +1031,14 @@ unsafe impl SystemParamState for ComponentsState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for ComponentsState {
type Item = &'a Components;
impl<'w, 's> SystemParamFetch<'w, 's> for ComponentsState {
type Item = &'w Components;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.components()
@ -1052,14 +1066,14 @@ unsafe impl SystemParamState for EntitiesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for EntitiesState {
type Item = &'a Entities;
impl<'w, 's> SystemParamFetch<'w, 's> for EntitiesState {
type Item = &'w Entities;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.entities()
@ -1087,14 +1101,14 @@ unsafe impl SystemParamState for BundlesState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for BundlesState {
type Item = &'a Bundles;
impl<'w, 's> SystemParamFetch<'w, 's> for BundlesState {
type Item = &'w Bundles;
#[inline]
unsafe fn get_param(
_state: &'a mut Self,
_state: &'s mut Self,
_system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
_change_tick: u32,
) -> Self::Item {
world.bundles()
@ -1127,13 +1141,13 @@ unsafe impl SystemParamState for SystemChangeTickState {
fn default_config() {}
}
impl<'a> SystemParamFetch<'a> for SystemChangeTickState {
impl<'w, 's> SystemParamFetch<'w, 's> for SystemChangeTickState {
type Item = SystemChangeTick;
unsafe fn get_param(
_state: &mut Self,
_state: &'s mut Self,
system_meta: &SystemMeta,
_world: &World,
_world: &'w World,
change_tick: u32,
) -> Self::Item {
SystemChangeTick {
@ -1154,15 +1168,15 @@ macro_rules! impl_system_param_tuple {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) {
impl<'w, 's, $($param: SystemParamFetch<'w, 's>),*> SystemParamFetch<'w, 's> for ($($param,)*) {
type Item = ($($param::Item,)*);
#[inline]
#[allow(clippy::unused_unit)]
unsafe fn get_param(
state: &'a mut Self,
state: &'s mut Self,
system_meta: &SystemMeta,
world: &'a World,
world: &'w World,
change_tick: u32,
) -> Self::Item {

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

@ -14,7 +14,7 @@ use bevy_ecs::{
system::{Query, Res, ResMut, SystemParam},
};
use bevy_reflect::Reflect;
use std::{ops::Range, sync::Arc};
use std::{marker::PhantomData, ops::Range, sync::Arc};
use thiserror::Error;
/// A queued command for the renderer
@ -164,18 +164,20 @@ pub enum DrawError {
}
#[derive(SystemParam)]
pub struct DrawContext<'a> {
pub pipelines: ResMut<'a, Assets<PipelineDescriptor>>,
pub shaders: ResMut<'a, Assets<Shader>>,
pub asset_render_resource_bindings: ResMut<'a, AssetRenderResourceBindings>,
pub pipeline_compiler: ResMut<'a, PipelineCompiler>,
pub render_resource_context: Res<'a, Box<dyn RenderResourceContext>>,
pub shared_buffers: ResMut<'a, SharedBuffers>,
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>,
pub pipeline_compiler: ResMut<'w, PipelineCompiler>,
pub render_resource_context: Res<'w, Box<dyn RenderResourceContext>>,
pub shared_buffers: ResMut<'w, SharedBuffers>,
#[system_param(ignore)]
pub current_pipeline: Option<Handle<PipelineDescriptor>>,
#[system_param(ignore)]
marker: PhantomData<&'s usize>,
}
impl<'a> DrawContext<'a> {
impl<'w, 's> DrawContext<'w, 's> {
pub fn get_uniform_buffer<T: RenderResource>(
&mut self,
render_resource: &T,

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

@ -13,10 +13,9 @@ use bevy_app::EventReader;
use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId};
use bevy_ecs::{
entity::Entity,
prelude::QueryState,
query::{Changed, Or, With},
system::{
BoxedSystem, ConfigurableSystem, Local, Query, QuerySet, RemovedComponents, Res, ResMut,
},
system::{BoxedSystem, ConfigurableSystem, Local, QuerySet, RemovedComponents, Res, ResMut},
world::World,
};
use bevy_utils::HashMap;
@ -437,8 +436,11 @@ fn render_resources_node_system<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();
@ -446,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);
}
@ -456,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,
@ -469,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;
}
@ -498,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;
@ -515,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;
@ -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

@ -8,9 +8,9 @@ use crate::{
use bevy_app::prelude::*;
use bevy_asset::{Assets, Handle, HandleUntyped};
use bevy_ecs::{
query::With,
query::{QueryState, With},
reflect::ReflectComponent,
system::{Query, QuerySet, Res},
system::{QuerySet, Res},
world::Mut,
};
use bevy_reflect::{Reflect, TypeUuid};
@ -62,8 +62,8 @@ pub fn draw_wireframes_system(
meshes: Res<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

@ -23,7 +23,7 @@ pub trait SpawnSceneCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>);
}
impl<'a> SpawnSceneCommands for Commands<'a> {
impl<'w, 's> SpawnSceneCommands for Commands<'w, 's> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) {
self.add(SpawnScene { scene_handle });
}
@ -45,7 +45,7 @@ pub trait SpawnSceneAsChildCommands {
fn spawn_scene(&mut self, scene: Handle<Scene>) -> &mut Self;
}
impl<'a, 'b> SpawnSceneAsChildCommands for ChildBuilder<'a, 'b> {
impl<'w, 's, 'a> SpawnSceneAsChildCommands for ChildBuilder<'w, 's, 'a> {
fn spawn_scene(&mut self, scene_handle: Handle<Scene>) -> &mut Self {
self.add_command(SpawnSceneAsChild {
scene_handle,

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

@ -40,8 +40,8 @@ pub struct PushChildren {
children: SmallVec<[Entity; 8]>,
}
pub struct ChildBuilder<'a, 'b> {
commands: &'b mut Commands<'a>,
pub struct ChildBuilder<'w, 's, 'a> {
commands: &'a mut Commands<'w, 's>,
push_children: PushChildren,
}
@ -71,14 +71,14 @@ impl Command for PushChildren {
}
}
impl<'a, 'b> ChildBuilder<'a, 'b> {
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'a, '_> {
impl<'w, 's, 'a> ChildBuilder<'w, 's, 'a> {
pub fn spawn_bundle(&mut self, bundle: impl Bundle) -> EntityCommands<'w, 's, '_> {
let e = self.commands.spawn_bundle(bundle);
self.push_children.children.push(e.id());
e
}
pub fn spawn(&mut self) -> EntityCommands<'a, '_> {
pub fn spawn(&mut self) -> EntityCommands<'w, 's, '_> {
let e = self.commands.spawn();
self.push_children.children.push(e.id());
e
@ -100,7 +100,7 @@ pub trait BuildChildren {
fn insert_children(&mut self, index: usize, children: &[Entity]) -> &mut Self;
}
impl<'a, 'b> BuildChildren for EntityCommands<'a, 'b> {
impl<'w, 's, 'a> BuildChildren for EntityCommands<'w, 's, 'a> {
fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self {
let parent = self.id();
let push_children = {

View file

@ -47,7 +47,7 @@ pub trait DespawnRecursiveExt {
fn despawn_recursive(&mut self);
}
impl<'a, 'b> DespawnRecursiveExt for EntityCommands<'a, 'b> {
impl<'w, 's, 'a> DespawnRecursiveExt for EntityCommands<'w, 's, 'a> {
/// Despawns the provided entity and its children.
fn despawn_recursive(&mut self) {
let entity = self.id();

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

@ -17,12 +17,12 @@ pub struct PlayerCount(usize);
///
/// In this example, it includes a query and a mutable resource.
#[derive(SystemParam)]
struct PlayerCounter<'a> {
players: Query<'a, &'static Player>,
count: ResMut<'a, PlayerCount>,
struct PlayerCounter<'w, 's> {
players: Query<'w, 's, &'static Player>,
count: ResMut<'w, PlayerCount>,
}
impl<'a> PlayerCounter<'a> {
impl<'w, 's> PlayerCounter<'w, 's> {
fn count(&mut self) {
self.count.0 = self.players.iter().len();
}

View file

@ -248,14 +248,18 @@ fn move_player(
fn focus_camera(
time: Res<Time>,
mut game: ResMut<Game>,
mut transforms: QuerySet<(Query<(&mut Transform, &Camera)>, Query<&Transform>)>,
mut transforms: QuerySet<(
QueryState<(&mut Transform, &Camera)>,
QueryState<&Transform>,
)>,
) {
const SPEED: f32 = 2.0;
// if there is both a player and a bonus, target the mid-point of them
if let (Some(player_entity), Some(bonus_entity)) = (game.player.entity, game.bonus.entity) {
let transform_query = transforms.q1();
if let (Ok(player_transform), Ok(bonus_transform)) = (
transforms.q1().get(player_entity),
transforms.q1().get(bonus_entity),
transform_query.get(player_entity),
transform_query.get(bonus_entity),
) {
game.camera_should_focus = player_transform
.translation
@ -280,7 +284,7 @@ fn focus_camera(
game.camera_is_focus += camera_motion;
}
// look at that new camera's actual focus
for (mut transform, camera) in transforms.q0_mut().iter_mut() {
for (mut transform, camera) in transforms.q0().iter_mut() {
if camera.name == Some(CAMERA_3D.to_string()) {
*transform = transform.looking_at(game.camera_is_focus, Vec3::Y);
}