make WorldQuery very flat (#5205)

# Objective

Simplify the worldquery trait hierarchy as much as possible by putting it all in one trait. If/when gats are stabilised this can be trivially migrated over to use them, although that's not why I made this PR, those reasons are:
- Moves all of the conceptually related unsafe code for a worldquery next to eachother
- Removes now unnecessary traits simplifying the "type system magic" in bevy_ecs

---

## Changelog

All methods/functions/types/consts on `FetchState` and `Fetch` traits have been moved to the `WorldQuery` trait and the other traits removed. `WorldQueryGats` now only contains an `Item` and `Fetch` assoc type.

## Migration Guide
Implementors should move items in impls to the `WorldQuery/Gats` traits and remove any `Fetch`/`FetchState` impls
Any use sites of items in the `Fetch`/`FetchState` traits should be updated to use the `WorldQuery` trait items instead


Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Boxy 2022-08-04 21:51:02 +00:00
parent 07d576987a
commit eabcd27d93
6 changed files with 1080 additions and 1230 deletions

View file

@ -79,6 +79,8 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
.unwrap_or_else(|_| panic!("Invalid `{}` attribute format", WORLD_QUERY_ATTRIBUTE_NAME));
}
let path = bevy_ecs_path();
let user_generics = ast.generics.clone();
let (user_impl_generics, user_ty_generics, user_where_clauses) = user_generics.split_for_impl();
let user_generics_with_world = {
@ -112,11 +114,6 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
let state_struct_name = Ident::new(&format!("{}State", struct_name), Span::call_site());
let fetch_type_alias = Ident::new("QueryFetch", Span::call_site());
let read_only_fetch_type_alias = Ident::new("ROQueryFetch", Span::call_site());
let item_type_alias = Ident::new("QueryItem", Span::call_site());
let read_only_item_type_alias = Ident::new("ROQueryItem", Span::call_site());
let fields = match &ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
@ -133,6 +130,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
let mut field_visibilities = Vec::new();
let mut field_idents = Vec::new();
let mut field_types = Vec::new();
let mut read_only_field_types = Vec::new();
for field in fields {
let WorldQueryFieldInfo { is_ignored, attrs } = read_world_query_field_info(field);
@ -147,7 +145,9 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
field_attrs.push(attrs);
field_visibilities.push(field.vis.clone());
field_idents.push(field_ident.clone());
field_types.push(field.ty.clone());
let field_ty = field.ty.clone();
field_types.push(quote!(#field_ty));
read_only_field_types.push(quote!(<#field_ty as #path::query::WorldQuery>::ReadOnly));
}
}
@ -155,46 +155,80 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
// `#[derive()]` is valid syntax
let derive_macro_call = quote! { #[derive(#derive_args)] };
let path = bevy_ecs_path();
let impl_fetch = |is_readonly: bool, fetch_struct_name: Ident, item_struct_name: Ident| {
let fetch_type_alias = if is_readonly {
&read_only_fetch_type_alias
let impl_fetch = |is_readonly: bool| {
let struct_name = if is_readonly {
&read_only_struct_name
} else {
&fetch_type_alias
&struct_name
};
let item_type_alias = if is_readonly {
&read_only_item_type_alias
let item_struct_name = if is_readonly {
&read_only_item_struct_name
} else {
&item_type_alias
&item_struct_name
};
let fetch_struct_name = if is_readonly {
&read_only_fetch_struct_name
} else {
&fetch_struct_name
};
let field_types = if is_readonly {
&read_only_field_types
} else {
&field_types
};
quote! {
#derive_macro_call
#[automatically_derived]
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#(#field_attrs)* #field_visibilities #field_idents: #path::query::#item_type_alias<'__w, #field_types>,)*
#(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQueryGats<'__w>>::Item,)*
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
}
#[derive(Clone)]
#[doc(hidden)]
#visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#field_idents: #path::query::#fetch_type_alias::<'__w, #field_types>,)*
#(#field_idents: <#field_types as #path::query::WorldQueryGats<'__w>>::Fetch,)*
#(#ignored_field_idents: #ignored_field_types,)*
}
// SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field
unsafe impl #user_impl_generics_with_world #path::query::Fetch<'__w>
for #fetch_struct_name #user_ty_generics_with_world #user_where_clauses_with_world {
type Item = #item_struct_name #user_ty_generics_with_world;
impl #user_impl_generics_with_world #path::query::WorldQueryGats<'__w>
for #struct_name #user_ty_generics #user_where_clauses {
type Item = #item_struct_name #user_ty_generics_with_world;
type Fetch = #fetch_struct_name #user_ty_generics_with_world;
}
unsafe impl #user_impl_generics #path::query::WorldQuery
for #struct_name #user_ty_generics #user_where_clauses {
type ReadOnly = #read_only_struct_name #user_ty_generics;
type State = #state_struct_name #user_ty_generics;
unsafe fn init(_world: &'__w #path::world::World, state: &Self::State, _last_change_tick: u32, _change_tick: u32) -> Self {
Self {
fn shrink<'__wlong: '__wshort, '__wshort>(
item: <#struct_name #user_ty_generics as #path::query::WorldQueryGats<'__wlong>>::Item
) -> <#struct_name #user_ty_generics as #path::query::WorldQueryGats<'__wshort>>::Item {
#item_struct_name {
#(
#field_idents: <#field_types>::shrink(item.#field_idents),
)*
#(
#ignored_field_idents: item.#ignored_field_idents,
)*
}
}
unsafe fn init_fetch<'__w>(
_world: &'__w #path::world::World,
state: &Self::State,
_last_change_tick: u32,
_change_tick: u32
) -> <Self as #path::query::WorldQueryGats<'__w>>::Fetch {
#fetch_struct_name {
#(#field_idents:
#path::query::#fetch_type_alias::<'__w, #field_types>::init(
<#field_types>::init_fetch(
_world,
&state.#field_idents,
_last_change_tick,
@ -205,138 +239,106 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
}
}
const IS_DENSE: bool = true #(&& #path::query::#fetch_type_alias::<'__w, #field_types>::IS_DENSE)*;
const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*;
const IS_ARCHETYPAL: bool = true #(&& #path::query::#fetch_type_alias::<'__w, #field_types>::IS_ARCHETYPAL)*;
const IS_ARCHETYPAL: bool = true #(&& <#field_types>::IS_ARCHETYPAL)*;
/// SAFETY: we call `set_archetype` for each member that implements `Fetch`
#[inline]
unsafe fn set_archetype(
&mut self,
unsafe fn set_archetype<'__w>(
_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch,
_state: &Self::State,
_archetype: &'__w #path::archetype::Archetype,
_tables: &'__w #path::storage::Tables
) {
#(self.#field_idents.set_archetype(&_state.#field_idents, _archetype, _tables);)*
#(<#field_types>::set_archetype(&mut _fetch.#field_idents, &_state.#field_idents, _archetype, _tables);)*
}
/// SAFETY: we call `set_table` for each member that implements `Fetch`
#[inline]
unsafe fn set_table(&mut self, _state: &Self::State, _table: &'__w #path::storage::Table) {
#(self.#field_idents.set_table(&_state.#field_idents, _table);)*
unsafe fn set_table<'__w>(
_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch,
_state: &Self::State,
_table: &'__w #path::storage::Table
) {
#(<#field_types>::set_table(&mut _fetch.#field_idents, &_state.#field_idents, _table);)*
}
/// SAFETY: we call `table_fetch` for each member that implements `Fetch`.
#[inline]
unsafe fn table_fetch(&mut self, _table_row: usize) -> Self::Item {
unsafe fn table_fetch<'__w>(
_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch,
_table_row: usize
) -> <Self as #path::query::WorldQueryGats<'__w>>::Item {
Self::Item {
#(#field_idents: self.#field_idents.table_fetch(_table_row),)*
#(#field_idents: <#field_types>::table_fetch(&mut _fetch.#field_idents, _table_row),)*
#(#ignored_field_idents: Default::default(),)*
}
}
/// SAFETY: we call `archetype_fetch` for each member that implements `Fetch`.
#[inline]
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) -> Self::Item {
unsafe fn archetype_fetch<'__w>(
_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch,
_archetype_index: usize
) -> <Self as #path::query::WorldQueryGats<'__w>>::Item {
Self::Item {
#(#field_idents: self.#field_idents.archetype_fetch(_archetype_index),)*
#(#field_idents: <#field_types>::archetype_fetch(&mut _fetch.#field_idents, _archetype_index),)*
#(#ignored_field_idents: Default::default(),)*
}
}
#[allow(unused_variables)]
#[inline]
unsafe fn table_filter_fetch(&mut self, _table_row: usize) -> bool {
true #(&& self.#field_idents.table_filter_fetch(_table_row))*
unsafe fn table_filter_fetch<'__w>(_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch, _table_row: usize) -> bool {
true #(&& <#field_types>::table_filter_fetch(&mut _fetch.#field_idents, _table_row))*
}
#[allow(unused_variables)]
#[inline]
unsafe fn archetype_filter_fetch(&mut self, _archetype_index: usize) -> bool {
true #(&& self.#field_idents.archetype_filter_fetch(_archetype_index))*
unsafe fn archetype_filter_fetch<'__w>(_fetch: &mut <Self as #path::query::WorldQueryGats<'__w>>::Fetch, _archetype_index: usize) -> bool {
true #(&& <#field_types>::archetype_filter_fetch(&mut _fetch.#field_idents, _archetype_index))*
}
fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) {
#( #path::query::#fetch_type_alias::<'static, #field_types> :: update_component_access(&state.#field_idents, _access); )*
#( <#field_types>::update_component_access(&state.#field_idents, _access); )*
}
fn update_archetype_component_access(state: &Self::State, _archetype: &#path::archetype::Archetype, _access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId>) {
fn update_archetype_component_access(
state: &Self::State,
_archetype: &#path::archetype::Archetype,
_access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId>
) {
#(
#path::query::#fetch_type_alias::<'static, #field_types>
:: update_archetype_component_access(&state.#field_idents, _archetype, _access);
<#field_types>::update_archetype_component_access(&state.#field_idents, _archetype, _access);
)*
}
}
}
};
let fetch_impl = impl_fetch(false, fetch_struct_name.clone(), item_struct_name.clone());
fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
#state_struct_name {
#(#field_idents: <#field_types>::init_state(world),)*
#(#ignored_field_idents: Default::default(),)*
}
}
let state_impl = quote! {
#[doc(hidden)]
#visibility struct #state_struct_name #user_impl_generics #user_where_clauses {
#(#field_idents: <#field_types as #path::query::WorldQuery>::State,)*
#(#ignored_field_idents: #ignored_field_types,)*
}
impl #user_impl_generics #path::query::FetchState for #state_struct_name #user_ty_generics #user_where_clauses {
fn init(world: &mut #path::world::World) -> Self {
#state_struct_name {
#(#field_idents: <<#field_types as #path::query::WorldQuery>::State as #path::query::FetchState>::init(world),)*
#(#ignored_field_idents: Default::default(),)*
fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool {
true #(&& <#field_types>::matches_component_set(&state.#field_idents, _set_contains_id))*
}
}
fn matches_component_set(&self, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool {
true #(&& self.#field_idents.matches_component_set(_set_contains_id))*
}
}
};
let read_only_fetch_impl = if fetch_struct_attributes.is_mutable {
impl_fetch(
true,
read_only_fetch_struct_name.clone(),
read_only_item_struct_name.clone(),
)
} else {
quote! {}
};
let read_only_world_query_impl = if fetch_struct_attributes.is_mutable {
let mutable_impl = impl_fetch(false);
let readonly_impl = if fetch_struct_attributes.is_mutable {
let world_query_impl = impl_fetch(true);
quote! {
#[automatically_derived]
#visibility struct #read_only_struct_name #user_impl_generics #user_where_clauses {
#( #field_idents: < #field_types as #path::query::WorldQuery >::ReadOnly, )*
#( #field_idents: #read_only_field_types, )*
#(#(#ignored_field_attrs)* #ignored_field_visibilities #ignored_field_idents: #ignored_field_types,)*
}
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
unsafe impl #user_impl_generics #path::query::WorldQuery for #read_only_struct_name #user_ty_generics #user_where_clauses {
type ReadOnly = Self;
type State = #state_struct_name #user_ty_generics;
fn shrink<'__wlong: '__wshort, '__wshort>(item: #path::query::#item_type_alias<'__wlong, Self>)
-> #path::query::#item_type_alias<'__wshort, Self> {
#read_only_item_struct_name {
#(
#field_idents : <
< #field_types as #path::query::WorldQuery >::ReadOnly as #path::query::WorldQuery
> :: shrink( item.#field_idents ),
)*
#(
#ignored_field_idents: item.#ignored_field_idents,
)*
}
}
}
impl #user_impl_generics_with_world #path::query::WorldQueryGats<'__w> for #read_only_struct_name #user_ty_generics #user_where_clauses {
type Fetch = #read_only_fetch_struct_name #user_ty_generics_with_world;
type _State = #state_struct_name #user_ty_generics;
}
#world_query_impl
}
} else {
quote! {}
@ -347,7 +349,7 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
// Double-check that the data fetched by `<_ as WorldQuery>::ReadOnly` is read-only.
// This is technically unnecessary as `<_ as WorldQuery>::ReadOnly: ReadOnlyWorldQuery`
// but to protect against future mistakes we assert the assoc type implements `ReadOnlyWorldQuery` anyway
#( assert_readonly::< < #field_types as #path::query::WorldQuery > :: ReadOnly >(); )*
#( assert_readonly::<#read_only_field_types>(); )*
}
} else {
quote! {
@ -363,38 +365,17 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
};
TokenStream::from(quote! {
#fetch_impl
#mutable_impl
#state_impl
#readonly_impl
#read_only_fetch_impl
#read_only_world_query_impl
// SAFETY: if the worldquery is mutable this defers to soundness of the `#field_types: WorldQuery` impl, otherwise
// if the world query is immutable then `#read_only_struct_name #user_ty_generics` is the same type as `#struct_name #user_ty_generics`
unsafe impl #user_impl_generics #path::query::WorldQuery for #struct_name #user_ty_generics #user_where_clauses {
type ReadOnly = #read_only_struct_name #user_ty_generics;
type State = #state_struct_name #user_ty_generics;
fn shrink<'__wlong: '__wshort, '__wshort>(item: #path::query::#item_type_alias<'__wlong, Self>)
-> #path::query::#item_type_alias<'__wshort, Self> {
#item_struct_name {
#(
#field_idents : < #field_types as #path::query::WorldQuery> :: shrink( item.#field_idents ),
)*
#(
#ignored_field_idents: item.#ignored_field_idents,
)*
}
}
#[doc(hidden)]
#visibility struct #state_struct_name #user_impl_generics #user_where_clauses {
#(#field_idents: <#field_types as #path::query::WorldQuery>::State,)*
#(#ignored_field_idents: #ignored_field_types,)*
}
impl #user_impl_generics_with_world #path::query::WorldQueryGats<'__w> for #struct_name #user_ty_generics #user_where_clauses {
type Fetch = #fetch_struct_name #user_ty_generics_with_world;
type _State = #state_struct_name #user_ty_generics;
}
/// SAFETY: each item in the struct is read only
/// SAFETY: we assert fields are readonly below
unsafe impl #user_impl_generics #path::query::ReadOnlyWorldQuery
for #read_only_struct_name #user_ty_generics #user_where_clauses {}
@ -417,9 +398,15 @@ pub fn derive_world_query_impl(ast: DeriveInput) -> TokenStream {
// workaround.
#[allow(dead_code)]
const _: () = {
fn dead_code_workaround #user_impl_generics (q: #struct_name #user_ty_generics) #user_where_clauses {
fn dead_code_workaround #user_impl_generics (
q: #struct_name #user_ty_generics,
q2: #read_only_struct_name #user_ty_generics
) #user_where_clauses {
#(q.#field_idents;)*
#(q.#ignored_field_idents;)*
#(q2.#field_idents;)*
#(q2.#ignored_field_idents;)*
}
};
})

File diff suppressed because it is too large Load diff

View file

@ -3,8 +3,7 @@ use crate::{
component::{Component, ComponentId, ComponentStorage, ComponentTicks, StorageType},
entity::Entity,
query::{
debug_checked_unreachable, Access, Fetch, FetchState, FilteredAccess, QueryFetch,
WorldQuery, WorldQueryGats,
debug_checked_unreachable, Access, FilteredAccess, QueryFetch, WorldQuery, WorldQueryGats,
},
storage::{ComponentSparseSet, Table, Tables},
world::World,
@ -44,65 +43,27 @@ use super::ReadOnlyWorldQuery;
/// ```
pub struct With<T>(PhantomData<T>);
impl<T: Component> WorldQueryGats<'_> for With<T> {
type Fetch = ();
type Item = ();
}
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
unsafe impl<T: Component> WorldQuery for With<T> {
type ReadOnly = Self;
type State = WithState<T>;
type State = ComponentId;
#[allow(clippy::semicolon_if_nothing_returned)]
fn shrink<'wlong: 'wshort, 'wshort>(
item: super::QueryItem<'wlong, Self>,
) -> super::QueryItem<'wshort, Self> {
item
}
}
/// The [`Fetch`] of [`With`].
#[doc(hidden)]
pub struct WithFetch<T> {
marker: PhantomData<T>,
}
/// The [`FetchState`] of [`With`].
#[doc(hidden)]
pub struct WithState<T> {
component_id: ComponentId,
marker: PhantomData<T>,
}
impl<T: Component> FetchState for WithState<T> {
fn init(world: &mut World) -> Self {
let component_id = world.init_component::<T>();
Self {
component_id,
marker: PhantomData,
}
_: <Self as WorldQueryGats<'wlong>>::Item,
) -> <Self as WorldQueryGats<'wshort>>::Item {
}
fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
set_contains_id(self.component_id)
}
}
impl<T: Component> WorldQueryGats<'_> for With<T> {
type Fetch = WithFetch<T>;
type _State = WithState<T>;
}
// SAFETY: no component access or archetype component access
unsafe impl<'w, T: Component> Fetch<'w> for WithFetch<T> {
type Item = ();
type State = WithState<T>;
unsafe fn init(
unsafe fn init_fetch(
_world: &World,
_state: &WithState<T>,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) -> Self {
Self {
marker: PhantomData,
}
) {
}
const IS_DENSE: bool = {
@ -115,50 +76,59 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithFetch<T> {
const IS_ARCHETYPAL: bool = true;
#[inline]
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
unsafe fn set_table(_fetch: &mut (), _state: &ComponentId, _table: &Table) {}
#[inline]
unsafe fn set_archetype(
&mut self,
_state: &Self::State,
_fetch: &mut (),
_state: &ComponentId,
_archetype: &Archetype,
_tables: &Tables,
) {
}
#[inline]
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) {}
unsafe fn archetype_fetch<'w>(
_fetch: &mut <Self as WorldQueryGats<'w>>::Fetch,
_archetype_index: usize,
) -> <Self as WorldQueryGats<'w>>::Item {
}
#[inline]
unsafe fn table_fetch(&mut self, _table_row: usize) {}
unsafe fn table_fetch<'w>(
_fetch: &mut <Self as WorldQueryGats<'w>>::Fetch,
_table_row: usize,
) -> <Self as WorldQueryGats<'w>>::Item {
}
#[inline]
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
access.add_with(state.component_id);
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
access.add_with(id);
}
#[inline]
fn update_archetype_component_access(
_state: &Self::State,
_state: &ComponentId,
_archetype: &Archetype,
_access: &mut Access<ArchetypeComponentId>,
) {
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(
&id: &ComponentId,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
set_contains_id(id)
}
}
// SAFETY: no component access or archetype component access
unsafe impl<T: Component> ReadOnlyWorldQuery for With<T> {}
impl<T> Clone for WithFetch<T> {
fn clone(&self) -> Self {
Self {
marker: self.marker,
}
}
}
impl<T> Copy for WithFetch<T> {}
/// Filter that selects entities without a component `T`.
///
/// This is the negation of [`With`].
@ -188,62 +158,19 @@ pub struct Without<T>(PhantomData<T>);
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
unsafe impl<T: Component> WorldQuery for Without<T> {
type ReadOnly = Self;
type State = WithoutState<T>;
type State = ComponentId;
#[allow(clippy::semicolon_if_nothing_returned)]
fn shrink<'wlong: 'wshort, 'wshort>(
item: super::QueryItem<'wlong, Self>,
) -> super::QueryItem<'wshort, Self> {
item
}
}
/// The [`Fetch`] of [`Without`].
#[doc(hidden)]
pub struct WithoutFetch<T> {
marker: PhantomData<T>,
}
/// The [`FetchState`] of [`Without`].
#[doc(hidden)]
pub struct WithoutState<T> {
component_id: ComponentId,
marker: PhantomData<T>,
}
impl<T: Component> FetchState for WithoutState<T> {
fn init(world: &mut World) -> Self {
let component_id = world.init_component::<T>();
Self {
component_id,
marker: PhantomData,
}
_: <Self as WorldQueryGats<'wlong>>::Item,
) -> <Self as WorldQueryGats<'wshort>>::Item {
}
fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
!set_contains_id(self.component_id)
}
}
impl<T: Component> WorldQueryGats<'_> for Without<T> {
type Fetch = WithoutFetch<T>;
type _State = WithoutState<T>;
}
// SAFETY: no component access or archetype component access
unsafe impl<'w, T: Component> Fetch<'w> for WithoutFetch<T> {
type Item = ();
type State = WithoutState<T>;
unsafe fn init(
unsafe fn init_fetch(
_world: &World,
_state: &WithoutState<T>,
_state: &ComponentId,
_last_change_tick: u32,
_change_tick: u32,
) -> Self {
WithoutFetch {
marker: PhantomData,
}
) {
}
const IS_DENSE: bool = {
@ -256,50 +183,64 @@ unsafe impl<'w, T: Component> Fetch<'w> for WithoutFetch<T> {
const IS_ARCHETYPAL: bool = true;
#[inline]
unsafe fn set_table(&mut self, _state: &Self::State, _table: &Table) {}
unsafe fn set_table(_fetch: &mut (), _state: &Self::State, _table: &Table) {}
#[inline]
unsafe fn set_archetype(
&mut self,
_state: &Self::State,
_fetch: &mut (),
_state: &ComponentId,
_archetype: &Archetype,
_tables: &Tables,
) {
}
#[inline]
unsafe fn archetype_fetch(&mut self, _archetype_index: usize) {}
unsafe fn archetype_fetch<'w>(
_fetch: &mut <Self as WorldQueryGats<'w>>::Fetch,
_archetype_index: usize,
) -> <Self as WorldQueryGats<'w>>::Item {
}
#[inline]
unsafe fn table_fetch(&mut self, _table_row: usize) {}
unsafe fn table_fetch<'w>(
_fetch: &mut <Self as WorldQueryGats<'w>>::Fetch,
_table_row: usize,
) -> <Self as WorldQueryGats<'w>>::Item {
}
#[inline]
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
access.add_without(state.component_id);
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
access.add_without(id);
}
#[inline]
fn update_archetype_component_access(
_state: &Self::State,
_state: &ComponentId,
_archetype: &Archetype,
_access: &mut Access<ArchetypeComponentId>,
) {
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(
&id: &ComponentId,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
!set_contains_id(id)
}
}
impl<T: Component> WorldQueryGats<'_> for Without<T> {
type Fetch = ();
type Item = ();
}
// SAFETY: no component access or archetype component access
unsafe impl<T: Component> ReadOnlyWorldQuery for Without<T> {}
impl<T> Clone for WithoutFetch<T> {
fn clone(&self) -> Self {
Self {
marker: self.marker,
}
}
}
impl<T> Copy for WithoutFetch<T> {}
/// 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
@ -333,104 +274,106 @@ impl<T> Copy for WithoutFetch<T> {}
#[derive(Clone, Copy)]
pub struct Or<T>(pub T);
/// The [`Fetch`] of [`Or`].
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct OrFetch<'w, T: Fetch<'w>> {
fetch: T,
pub struct OrFetch<'w, T: WorldQuery> {
fetch: QueryFetch<'w, T>,
matches: bool,
_marker: PhantomData<&'w ()>,
}
impl<'w, T: WorldQuery> Copy for OrFetch<'w, T> where QueryFetch<'w, T>: Copy {}
impl<'w, T: WorldQuery> Clone for OrFetch<'w, T>
where
QueryFetch<'w, T>: Clone,
{
fn clone(&self) -> Self {
Self {
fetch: self.fetch.clone(),
matches: self.matches,
}
}
}
macro_rules! impl_query_filter_tuple {
($(($filter: ident, $state: ident)),*) => {
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'w, $($filter: WorldQuery),*> WorldQueryGats<'w> for Or<($($filter,)*)> {
type Fetch = ($(OrFetch<'w, $filter>,)*);
type Item = bool;
}
#[allow(unused_variables)]
#[allow(non_snake_case)]
#[allow(clippy::unused_unit)]
// SAFETY: defers to soundness of `$filter: WorldQuery` impl
unsafe impl<$($filter: WorldQuery),*> WorldQuery for Or<($($filter,)*)> {
type ReadOnly = Or<($($filter::ReadOnly,)*)>;
type State = Or<($($filter::State,)*)>;
type State = ($($filter::State,)*);
fn shrink<'wlong: 'wshort, 'wshort>(item: super::QueryItem<'wlong, Self>) -> super::QueryItem<'wshort, Self> {
item
}
}
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<'w, $($filter: WorldQueryGats<'w>),*> WorldQueryGats<'w> for Or<($($filter,)*)> {
type Fetch = Or<($(OrFetch<'w, QueryFetch<'w, $filter>>,)*)>;
type _State = Or<($($filter::_State,)*)>;
}
#[allow(unused_variables)]
#[allow(non_snake_case)]
// SAFETY: update_component_access and update_archetype_component_access are called for each item in the tuple
unsafe impl<'w, $($filter: Fetch<'w>),*> Fetch<'w> for Or<($(OrFetch<'w, $filter>,)*)> {
type State = Or<($(<$filter as Fetch<'w>>::State,)*)>;
type Item = bool;
const IS_DENSE: bool = true $(&& $filter::IS_DENSE)*;
const IS_ARCHETYPAL: bool = true $(&& $filter::IS_ARCHETYPAL)*;
unsafe fn init(world: &'w World, state: & Or<($(<$filter as Fetch<'w>>::State,)*)>, last_change_tick: u32, change_tick: u32) -> Self {
let ($($filter,)*) = &state.0;
Or(($(OrFetch {
fetch: <$filter as Fetch<'w>>::init(world, $filter, last_change_tick, change_tick),
unsafe fn init_fetch<'w>(world: &'w World, state: &Self::State, last_change_tick: u32, change_tick: u32) -> <Self as WorldQueryGats<'w>>::Fetch {
let ($($filter,)*) = state;
($(OrFetch {
fetch: $filter::init_fetch(world, $filter, last_change_tick, change_tick),
matches: false,
_marker: PhantomData,
},)*))
},)*)
}
#[inline]
unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) {
let ($($filter,)*) = &mut self.0;
let ($($state,)*) = &state.0;
unsafe fn set_table<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, state: &Self::State, table: &'w Table) {
let ($($filter,)*) = fetch;
let ($($state,)*) = state;
$(
$filter.matches = $state.matches_component_set(&|id| table.has_column(id));
$filter.matches = $filter::matches_component_set($state, &|id| table.has_column(id));
if $filter.matches {
$filter.fetch.set_table($state, table);
$filter::set_table(&mut $filter.fetch, $state, table);
}
)*
}
#[inline]
unsafe fn set_archetype(&mut self, state: & Self::State, archetype: &'w Archetype, tables: &'w Tables) {
let ($($filter,)*) = &mut self.0;
let ($($state,)*) = &state.0;
unsafe fn set_archetype<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) {
let ($($filter,)*) = fetch;
let ($($state,)*) = state;
$(
$filter.matches = $state.matches_component_set(&|id| archetype.contains(id));
$filter.matches = $filter::matches_component_set($state, &|id| archetype.contains(id));
if $filter.matches {
$filter.fetch.set_archetype($state, archetype, tables);
$filter::set_archetype(&mut $filter.fetch, $state, archetype, tables);
}
)*
}
#[inline]
unsafe fn table_fetch(&mut self, table_row: usize) -> bool {
let ($($filter,)*) = &mut self.0;
false $(|| ($filter.matches && $filter.fetch.table_filter_fetch(table_row)))*
unsafe fn table_fetch<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, table_row: usize) -> <Self as WorldQueryGats<'w>>::Item {
let ($($filter,)*) = fetch;
false $(|| ($filter.matches && $filter::table_filter_fetch(&mut $filter.fetch, table_row)))*
}
#[inline]
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool {
let ($($filter,)*) = &mut self.0;
false $(|| ($filter.matches && $filter.fetch.archetype_filter_fetch(archetype_index)))*
unsafe fn archetype_fetch<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, archetype_index: usize) -> <Self as WorldQueryGats<'w>>::Item {
let ($($filter,)*) = fetch;
false $(|| ($filter.matches && $filter::archetype_filter_fetch(&mut $filter.fetch, archetype_index)))*
}
#[inline]
unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool {
self.table_fetch(table_row)
unsafe fn table_filter_fetch(fetch: &mut QueryFetch<'_, Self>, table_row: usize) -> bool {
Self::table_fetch(fetch, table_row)
}
#[inline]
unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool {
self.archetype_fetch(archetype_index)
unsafe fn archetype_filter_fetch(fetch: &mut QueryFetch<'_, Self>, archetype_index: usize) -> bool {
Self::archetype_fetch(fetch, archetype_index)
}
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
let ($($filter,)*) = &state.0;
let ($($filter,)*) = state;
// We do not unconditionally add `$filter`'s `with`/`without` accesses to `access`
// as this would be unsound. For example the following two queries should conflict:
@ -462,21 +405,17 @@ macro_rules! impl_query_filter_tuple {
}
fn update_archetype_component_access(state: &Self::State, archetype: &Archetype, access: &mut Access<ArchetypeComponentId>) {
let ($($filter,)*) = &state.0;
let ($($filter,)*) = state;
$($filter::update_archetype_component_access($filter, archetype, access);)*
}
}
#[allow(unused_variables)]
#[allow(non_snake_case)]
impl<$($filter: FetchState),*> FetchState for Or<($($filter,)*)> {
fn init(world: &mut World) -> Self {
Or(($($filter::init(world),)*))
fn init_state(world: &mut World) -> Self::State {
($($filter::init_state(world),)*)
}
fn matches_component_set(&self, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
let ($($filter,)*) = &self.0;
false $(|| $filter.matches_component_set(_set_contains_id))*
fn matches_component_set(_state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
let ($($filter,)*) = _state;
false $(|| $filter::matches_component_set($filter, _set_contains_id))*
}
}
@ -491,8 +430,6 @@ macro_rules! impl_tick_filter {
(
$(#[$meta:meta])*
$name: ident,
$(#[$state_meta:meta])*
$state_name: ident,
$(#[$fetch_meta:meta])*
$fetch_name: ident,
$is_detected: expr
@ -512,53 +449,22 @@ macro_rules! impl_tick_filter {
change_tick: u32,
}
#[doc(hidden)]
$(#[$state_meta])*
pub struct $state_name<T> {
component_id: ComponentId,
marker: PhantomData<T>,
}
// SAFETY: `ROQueryFetch<Self>` is the same as `QueryFetch<Self>`
unsafe impl<T: Component> WorldQuery for $name<T> {
type ReadOnly = Self;
type State = $state_name<T>;
type State = ComponentId;
fn shrink<'wlong: 'wshort, 'wshort>(item: super::QueryItem<'wlong, Self>) -> super::QueryItem<'wshort, Self> {
item
}
}
impl<T: Component> FetchState for $state_name<T> {
fn init(world: &mut World) -> Self {
Self {
component_id: world.init_component::<T>(),
marker: PhantomData,
}
}
fn matches_component_set(&self, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
set_contains_id(self.component_id)
}
}
impl<'w, T: Component> WorldQueryGats<'w> for $name<T> {
type Fetch = $fetch_name<'w, T>;
type _State = $state_name<T>;
}
// SAFETY: this reads the T component. archetype component access and component access are updated to reflect that
unsafe impl<'w, T: Component> Fetch<'w> for $fetch_name<'w, T> {
type State = $state_name<T>;
type Item = bool;
unsafe fn init(world: &'w World, state: & $state_name<T>, last_change_tick: u32, change_tick: u32) -> Self {
Self {
unsafe fn init_fetch<'w>(world: &'w World, &id: &ComponentId, last_change_tick: u32, change_tick: u32) -> <Self as WorldQueryGats<'w>>::Fetch {
QueryFetch::<'w, Self> {
table_ticks: None,
entities: None,
entity_table_rows: None,
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet)
.then(|| world.storages().sparse_sets.get(state.component_id).unwrap()),
.then(|| world.storages().sparse_sets.get(id).unwrap()),
marker: PhantomData,
last_change_tick,
change_tick,
@ -574,74 +480,87 @@ macro_rules! impl_tick_filter {
const IS_ARCHETYPAL: bool = false;
unsafe fn set_table(&mut self, state: &Self::State, table: &'w Table) {
self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into());
unsafe fn set_table<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, &id: &ComponentId, table: &'w Table) {
fetch.table_ticks = Some(table.get_column(id).unwrap().get_ticks_slice().into());
}
unsafe fn set_archetype(&mut self, state: &Self::State, archetype: &'w Archetype, tables: &'w Tables) {
unsafe fn set_archetype<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, &id: &ComponentId, archetype: &'w Archetype, tables: &'w Tables) {
match T::Storage::STORAGE_TYPE {
StorageType::Table => {
self.entity_table_rows = Some(archetype.entity_table_rows().into());
fetch.entity_table_rows = Some(archetype.entity_table_rows().into());
let table = &tables[archetype.table_id()];
self.table_ticks = Some(table.get_column(state.component_id).unwrap().get_ticks_slice().into());
fetch.table_ticks = Some(table.get_column(id).unwrap().get_ticks_slice().into());
}
StorageType::SparseSet => self.entities = Some(archetype.entities().into()),
StorageType::SparseSet => fetch.entities = Some(archetype.entities().into()),
}
}
unsafe fn table_fetch(&mut self, table_row: usize) -> bool {
$is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick)
unsafe fn table_fetch<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, table_row: usize) -> <Self as WorldQueryGats<'w>>::Item {
$is_detected(&*(fetch.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), fetch.last_change_tick, fetch.change_tick)
}
unsafe fn archetype_fetch(&mut self, archetype_index: usize) -> bool {
unsafe fn archetype_fetch<'w>(fetch: &mut <Self as WorldQueryGats<'w>>::Fetch, archetype_index: usize) -> <Self as WorldQueryGats<'w>>::Item {
match T::Storage::STORAGE_TYPE {
StorageType::Table => {
let table_row = *self.entity_table_rows.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index);
$is_detected(&*(self.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), self.last_change_tick, self.change_tick)
let table_row = *fetch.entity_table_rows.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index);
$is_detected(&*(fetch.table_ticks.unwrap_or_else(|| debug_checked_unreachable()).get(table_row)).deref(), fetch.last_change_tick, fetch.change_tick)
}
StorageType::SparseSet => {
let entity = *self.entities.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index);
let ticks = self
let entity = *fetch.entities.unwrap_or_else(|| debug_checked_unreachable()).get(archetype_index);
let ticks = fetch
.sparse_set
.unwrap_or_else(|| debug_checked_unreachable())
.get_ticks(entity)
.map(|ticks| &*ticks.get())
.cloned()
.unwrap();
$is_detected(&ticks, self.last_change_tick, self.change_tick)
$is_detected(&ticks, fetch.last_change_tick, fetch.change_tick)
}
}
}
#[inline]
unsafe fn table_filter_fetch(&mut self, table_row: usize) -> bool {
self.table_fetch(table_row)
unsafe fn table_filter_fetch(fetch: &mut QueryFetch<'_, Self>, table_row: usize) -> bool {
Self::table_fetch(fetch, table_row)
}
#[inline]
unsafe fn archetype_filter_fetch(&mut self, archetype_index: usize) -> bool {
self.archetype_fetch(archetype_index)
unsafe fn archetype_filter_fetch(fetch: &mut QueryFetch<'_, Self>, archetype_index: usize) -> bool {
Self::archetype_fetch(fetch, archetype_index)
}
#[inline]
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_write(state.component_id) {
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
if access.access().has_write(id) {
panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
std::any::type_name::<T>());
}
access.add_read(state.component_id);
access.add_read(id);
}
#[inline]
fn update_archetype_component_access(
state: &Self::State,
&id: &ComponentId,
archetype: &Archetype,
access: &mut Access<ArchetypeComponentId>,
) {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(state.component_id) {
if let Some(archetype_component_id) = archetype.get_archetype_component_id(id) {
access.add_read(archetype_component_id);
}
}
fn init_state(world: &mut World) -> ComponentId {
world.init_component::<T>()
}
fn matches_component_set(&id: &ComponentId, set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
set_contains_id(id)
}
}
impl<'w, T: Component> WorldQueryGats<'w> for $name<T> {
type Fetch = $fetch_name<'w, T>;
type Item = bool;
}
/// SAFETY: read-only access
@ -693,9 +612,6 @@ impl_tick_filter!(
/// # bevy_ecs::system::assert_is_system(print_add_name_component);
/// ```
Added,
/// The [`FetchState`] of [`Added`].
AddedState,
/// The [`Fetch`] of [`Added`].
AddedFetch,
ComponentTicks::is_added
);
@ -733,9 +649,6 @@ impl_tick_filter!(
/// # bevy_ecs::system::assert_is_system(print_moving_objects_system);
/// ```
Changed,
/// The [`FetchState`] of [`Changed`].
ChangedState,
/// The [`Fetch`] of [`Changed`].
ChangedFetch,
ComponentTicks::is_changed
);
@ -745,7 +658,7 @@ impl_tick_filter!(
/// This is needed to implement [`ExactSizeIterator`](std::iter::ExactSizeIterator) for
/// [`QueryIter`](crate::query::QueryIter) that contains archetype-level filters.
///
/// The trait must only be implement for filters where its corresponding [`Fetch::IS_ARCHETYPAL`](crate::query::Fetch::IS_ARCHETYPAL)
/// The trait must only be implement for filters where its corresponding [`WorldQuery::IS_ARCHETYPAL`](crate::query::WorldQuery::IS_ARCHETYPAL)
/// is [`prim@true`]. As such, only the [`With`] and [`Without`] filters can implement the trait.
/// [Tuples](prim@tuple) and [`Or`] filters are automatically implemented with the trait only if its containing types
/// also implement the same trait.

View file

@ -2,7 +2,7 @@ use crate::{
archetype::{ArchetypeId, Archetypes},
entity::{Entities, Entity},
prelude::World,
query::{ArchetypeFilter, Fetch, QueryState, WorldQuery},
query::{ArchetypeFilter, QueryState, WorldQuery},
storage::{TableId, Tables},
};
use std::{borrow::Borrow, iter::FusedIterator, marker::PhantomData, mem::MaybeUninit};
@ -63,7 +63,7 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> Iterator for QueryIter<'w, 's, Q, F>
.map(|id| self.archetypes[*id].len())
.sum();
let archetype_query = Q::Fetch::IS_ARCHETYPAL && F::Fetch::IS_ARCHETYPAL;
let archetype_query = Q::IS_ARCHETYPAL && F::IS_ARCHETYPAL;
let min_size = if archetype_query { max_size } else { 0 };
(min_size, Some(max_size))
}
@ -104,13 +104,13 @@ where
last_change_tick: u32,
change_tick: u32,
) -> QueryManyIter<'w, 's, Q, F, I> {
let fetch = Q::Fetch::init(
let fetch = Q::init_fetch(
world,
&query_state.fetch_state,
last_change_tick,
change_tick,
);
let filter = F::Fetch::init(
let filter = F::init_fetch(
world,
&query_state.filter_state,
last_change_tick,
@ -153,19 +153,25 @@ where
// SAFETY: `archetype` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
self.fetch
.set_archetype(&self.query_state.fetch_state, archetype, self.tables);
Q::set_archetype(
&mut self.fetch,
&self.query_state.fetch_state,
archetype,
self.tables,
);
// SAFETY: `table` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
self.filter
.set_archetype(&self.query_state.filter_state, archetype, self.tables);
F::set_archetype(
&mut self.filter,
&self.query_state.filter_state,
archetype,
self.tables,
);
// SAFETY: set_archetype was called prior.
// `location.index` is an archetype index row in range of the current archetype, because if it was not, the match above would have `continue`d
if self.filter.archetype_filter_fetch(location.index) {
if F::archetype_filter_fetch(&mut self.filter, location.index) {
// SAFETY: set_archetype was called prior, `location.index` is an archetype index in range of the current archetype
return Some(self.fetch.archetype_fetch(location.index));
return Some(Q::archetype_fetch(&mut self.fetch, location.index));
}
}
None
@ -370,7 +376,7 @@ where
let smallest = K.min(max_size - K);
let max_combinations = choose(max_size, smallest);
let archetype_query = F::Fetch::IS_ARCHETYPAL && Q::Fetch::IS_ARCHETYPAL;
let archetype_query = F::IS_ARCHETYPAL && Q::IS_ARCHETYPAL;
let known_max = max_combinations.unwrap_or(usize::MAX);
let min_combinations = if archetype_query { known_max } else { 0 };
(min_combinations, max_combinations)
@ -445,7 +451,7 @@ where
}
impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
const IS_DENSE: bool = Q::Fetch::IS_DENSE && F::Fetch::IS_DENSE;
const IS_DENSE: bool = Q::IS_DENSE && F::IS_DENSE;
unsafe fn init_empty(
world: &'w World,
@ -466,13 +472,13 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
last_change_tick: u32,
change_tick: u32,
) -> Self {
let fetch = Q::Fetch::init(
let fetch = Q::init_fetch(
world,
&query_state.fetch_state,
last_change_tick,
change_tick,
);
let filter = F::Fetch::init(
let filter = F::init_fetch(
world,
&query_state.filter_state,
last_change_tick,
@ -494,9 +500,9 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
unsafe fn peek_last(&mut self) -> Option<QueryItem<'w, Q>> {
if self.current_index > 0 {
if Self::IS_DENSE {
Some(self.fetch.table_fetch(self.current_index - 1))
Some(Q::table_fetch(&mut self.fetch, self.current_index - 1))
} else {
Some(self.fetch.archetype_fetch(self.current_index - 1))
Some(Q::archetype_fetch(&mut self.fetch, self.current_index - 1))
}
} else {
None
@ -524,8 +530,8 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
let table = &tables[*table_id];
// SAFETY: `table` is from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
self.fetch.set_table(&query_state.fetch_state, table);
self.filter.set_table(&query_state.filter_state, table);
Q::set_table(&mut self.fetch, &query_state.fetch_state, table);
F::set_table(&mut self.filter, &query_state.filter_state, table);
self.current_len = table.len();
self.current_index = 0;
continue;
@ -533,14 +539,14 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
// SAFETY: set_table was called prior.
// `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed.
if !self.filter.table_filter_fetch(self.current_index) {
if !F::table_filter_fetch(&mut self.filter, self.current_index) {
self.current_index += 1;
continue;
}
// SAFETY: set_table was called prior.
// `current_index` is a table row in range of the current table, because if it was not, then the if above would have been executed.
let item = self.fetch.table_fetch(self.current_index);
let item = Q::table_fetch(&mut self.fetch, self.current_index);
self.current_index += 1;
return Some(item);
@ -552,10 +558,13 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
let archetype = &archetypes[*archetype_id];
// SAFETY: `archetype` and `tables` are from the world that `fetch/filter` were created for,
// `fetch_state`/`filter_state` are the states that `fetch/filter` were initialized with
self.fetch
.set_archetype(&query_state.fetch_state, archetype, tables);
self.filter
.set_archetype(&query_state.filter_state, archetype, tables);
Q::set_archetype(&mut self.fetch, &query_state.fetch_state, archetype, tables);
F::set_archetype(
&mut self.filter,
&query_state.filter_state,
archetype,
tables,
);
self.current_len = archetype.len();
self.current_index = 0;
continue;
@ -563,14 +572,14 @@ impl<'w, 's, Q: WorldQuery, F: WorldQuery> QueryIterationCursor<'w, 's, Q, F> {
// SAFETY: set_archetype was called prior.
// `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed.
if !self.filter.archetype_filter_fetch(self.current_index) {
if !F::archetype_filter_fetch(&mut self.filter, self.current_index) {
self.current_index += 1;
continue;
}
// SAFETY: set_archetype was called prior, `current_index` is an archetype index in range of the current archetype
// `current_index` is an archetype index row in range of the current archetype, because if it was not, then the if above would have been executed.
let item = self.fetch.archetype_fetch(self.current_index);
let item = Q::archetype_fetch(&mut self.fetch, self.current_index);
self.current_index += 1;
return Some(item);
}

View file

@ -3,9 +3,7 @@ use crate::{
component::ComponentId,
entity::Entity,
prelude::FromWorld,
query::{
Access, Fetch, FetchState, FilteredAccess, QueryCombinationIter, QueryIter, WorldQuery,
},
query::{Access, FilteredAccess, QueryCombinationIter, QueryIter, WorldQuery},
storage::TableId,
world::{World, WorldId},
};
@ -15,7 +13,7 @@ use bevy_utils::tracing::Instrument;
use fixedbitset::FixedBitSet;
use std::{borrow::Borrow, fmt};
use super::{NopWorldQuery, QueryFetch, QueryItem, QueryManyIter, ROQueryItem};
use super::{NopWorldQuery, QueryItem, QueryManyIter, ROQueryItem};
/// Provides scoped access to a [`World`] state according to a given [`WorldQuery`] and query filter.
#[repr(C)]
@ -84,20 +82,17 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
/// 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);
let fetch_state = Q::init_state(world);
let filter_state = F::init_state(world);
let mut component_access = FilteredAccess::default();
QueryFetch::<'static, Q>::update_component_access(&fetch_state, &mut component_access);
Q::update_component_access(&fetch_state, &mut component_access);
// Use a temporary empty FilteredAccess for filters. This prevents them from conflicting with the
// main Query's `fetch_state` access. Filters are allowed to conflict with the main query fetch
// because they are evaluated *before* a specific reference is constructed.
let mut filter_component_access = FilteredAccess::default();
QueryFetch::<'static, F>::update_component_access(
&filter_state,
&mut filter_component_access,
);
F::update_component_access(&filter_state, &mut filter_component_access);
// Merge the temporary filter access with the main access. This ensures that filter access is
// properly considered in a global "cross-query" context (both within systems and across systems).
@ -160,19 +155,15 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
/// Creates a new [`Archetype`].
pub fn new_archetype(&mut self, archetype: &Archetype) {
if self
.fetch_state
.matches_component_set(&|id| archetype.contains(id))
&& self
.filter_state
.matches_component_set(&|id| archetype.contains(id))
if Q::matches_component_set(&self.fetch_state, &|id| archetype.contains(id))
&& F::matches_component_set(&self.filter_state, &|id| archetype.contains(id))
{
QueryFetch::<'static, Q>::update_archetype_component_access(
Q::update_archetype_component_access(
&self.fetch_state,
archetype,
&mut self.archetype_component_access,
);
QueryFetch::<'static, F>::update_archetype_component_access(
F::update_archetype_component_access(
&self.filter_state,
archetype,
&mut self.archetype_component_access,
@ -408,19 +399,23 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
return Err(QueryEntityError::QueryDoesNotMatch(entity));
}
let archetype = &world.archetypes[location.archetype_id];
let mut fetch =
<QueryFetch<Q> as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = <QueryFetch<F> as Fetch>::init(
world,
&self.filter_state,
last_change_tick,
change_tick,
);
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = F::init_fetch(world, &self.filter_state, last_change_tick, change_tick);
fetch.set_archetype(&self.fetch_state, archetype, &world.storages().tables);
filter.set_archetype(&self.filter_state, archetype, &world.storages().tables);
if filter.archetype_filter_fetch(location.index) {
Ok(fetch.archetype_fetch(location.index))
Q::set_archetype(
&mut fetch,
&self.fetch_state,
archetype,
&world.storages().tables,
);
F::set_archetype(
&mut filter,
&self.filter_state,
archetype,
&world.storages().tables,
);
if F::archetype_filter_fetch(&mut filter, location.index) {
Ok(Q::archetype_fetch(&mut fetch, location.index))
} else {
Err(QueryEntityError::QueryDoesNotMatch(entity))
}
@ -909,27 +904,21 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
) {
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
let mut fetch =
<QueryFetch<Q> as Fetch>::init(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = <QueryFetch<F> as Fetch>::init(
world,
&self.filter_state,
last_change_tick,
change_tick,
);
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_change_tick, change_tick);
let mut filter = F::init_fetch(world, &self.filter_state, last_change_tick, change_tick);
if <QueryFetch<'static, Q>>::IS_DENSE && <QueryFetch<'static, F>>::IS_DENSE {
if Q::IS_DENSE && F::IS_DENSE {
let tables = &world.storages().tables;
for table_id in &self.matched_table_ids {
let table = &tables[*table_id];
fetch.set_table(&self.fetch_state, table);
filter.set_table(&self.filter_state, table);
Q::set_table(&mut fetch, &self.fetch_state, table);
F::set_table(&mut filter, &self.filter_state, table);
for table_index in 0..table.len() {
if !filter.table_filter_fetch(table_index) {
if !F::table_filter_fetch(&mut filter, table_index) {
continue;
}
let item = fetch.table_fetch(table_index);
let item = Q::table_fetch(&mut fetch, table_index);
func(item);
}
}
@ -938,14 +927,14 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
let tables = &world.storages().tables;
for archetype_id in &self.matched_archetype_ids {
let archetype = &archetypes[*archetype_id];
fetch.set_archetype(&self.fetch_state, archetype, tables);
filter.set_archetype(&self.filter_state, archetype, tables);
Q::set_archetype(&mut fetch, &self.fetch_state, archetype, tables);
F::set_archetype(&mut filter, &self.filter_state, archetype, tables);
for archetype_index in 0..archetype.len() {
if !filter.archetype_filter_fetch(archetype_index) {
if !F::archetype_filter_fetch(&mut filter, archetype_index) {
continue;
}
func(fetch.archetype_fetch(archetype_index));
func(Q::archetype_fetch(&mut fetch, archetype_index));
}
}
}
@ -979,7 +968,7 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
// QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
ComputeTaskPool::get().scope(|scope| {
if <QueryFetch<'static, Q>>::IS_DENSE && <QueryFetch<'static, F>>::IS_DENSE {
if Q::IS_DENSE && F::IS_DENSE {
let tables = &world.storages().tables;
for table_id in &self.matched_table_ids {
let table = &tables[*table_id];
@ -988,13 +977,13 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
let func = func.clone();
let len = batch_size.min(table.len() - offset);
let task = async move {
let mut fetch = <QueryFetch<Q> as Fetch>::init(
let mut fetch = Q::init_fetch(
world,
&self.fetch_state,
last_change_tick,
change_tick,
);
let mut filter = <QueryFetch<F> as Fetch>::init(
let mut filter = F::init_fetch(
world,
&self.filter_state,
last_change_tick,
@ -1002,13 +991,13 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
);
let tables = &world.storages().tables;
let table = &tables[*table_id];
fetch.set_table(&self.fetch_state, table);
filter.set_table(&self.filter_state, table);
Q::set_table(&mut fetch, &self.fetch_state, table);
F::set_table(&mut filter, &self.filter_state, table);
for table_index in offset..offset + len {
if !filter.table_filter_fetch(table_index) {
if !F::table_filter_fetch(&mut filter, table_index) {
continue;
}
let item = fetch.table_fetch(table_index);
let item = Q::table_fetch(&mut fetch, table_index);
func(item);
}
};
@ -1034,13 +1023,13 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
let func = func.clone();
let len = batch_size.min(archetype.len() - offset);
let task = async move {
let mut fetch = <QueryFetch<Q> as Fetch>::init(
let mut fetch = Q::init_fetch(
world,
&self.fetch_state,
last_change_tick,
change_tick,
);
let mut filter = <QueryFetch<F> as Fetch>::init(
let mut filter = F::init_fetch(
world,
&self.filter_state,
last_change_tick,
@ -1048,14 +1037,14 @@ impl<Q: WorldQuery, F: WorldQuery> QueryState<Q, F> {
);
let tables = &world.storages().tables;
let archetype = &world.archetypes[*archetype_id];
fetch.set_archetype(&self.fetch_state, archetype, tables);
filter.set_archetype(&self.filter_state, archetype, tables);
Q::set_archetype(&mut fetch, &self.fetch_state, archetype, tables);
F::set_archetype(&mut filter, &self.filter_state, archetype, tables);
for archetype_index in offset..offset + len {
if !filter.archetype_filter_fetch(archetype_index) {
if !F::archetype_filter_fetch(&mut filter, archetype_index) {
continue;
}
func(fetch.archetype_fetch(archetype_index));
func(Q::archetype_fetch(&mut fetch, archetype_index));
}
};

View file

@ -25,7 +25,7 @@ error[E0277]: the trait bound `&'static mut Foo: ReadOnlyWorldQuery` is not sati
= note: `ReadOnlyWorldQuery` is implemented for `&'static Foo`, but not for `&'static mut Foo`
= note: required because of the requirements on the impl of `ReadOnlySystemParamFetch` for `QueryState<&'static mut Foo>`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `ReadOnlySystemParamFetch` for `_::FetchState<(QueryState<&'static mut Foo>,)>`
= note: required because of the requirements on the impl of `ReadOnlySystemParamFetch` for `FetchState<(QueryState<&'static mut Foo>,)>`
note: required by a bound in `assert_readonly`
--> tests/ui/system_param_derive_readonly.rs:23:32
|