Skip to content

Split WorldQuery into WorldQueryData and WorldQueryFilter #9918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/bevy_core/src/name.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bevy_ecs::{
component::Component, entity::Entity, query::WorldQuery, reflect::ReflectComponent,
};
use bevy_ecs::query::WorldQueryData;
use bevy_ecs::{component::Component, entity::Entity, reflect::ReflectComponent};

use bevy_reflect::std_traits::ReflectDefault;
use bevy_reflect::Reflect;
use bevy_utils::AHasher;
Expand Down Expand Up @@ -102,7 +102,7 @@ impl std::fmt::Debug for Name {
/// }
/// # bevy_ecs::system::assert_is_system(increment_score);
/// ```
#[derive(WorldQuery)]
#[derive(WorldQueryData)]
pub struct DebugName {
/// A [`Name`] that the entity might have that is displayed if available.
pub name: Option<&'static Name>,
Expand Down
483 changes: 0 additions & 483 deletions crates/bevy_ecs/macros/src/fetch.rs

This file was deleted.

23 changes: 17 additions & 6 deletions crates/bevy_ecs/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
extern crate proc_macro;

mod component;
mod fetch;
mod states;
mod world_query;
mod world_query_data;
mod world_query_filter;

use crate::fetch::derive_world_query_impl;
use crate::{
world_query_data::derive_world_query_data_impl,
world_query_filter::derive_world_query_filter_impl,
};
use bevy_macro_utils::{derive_label, ensure_no_collision, get_struct_fields, BevyManifest};
use proc_macro::TokenStream;
use proc_macro2::Span;
Expand Down Expand Up @@ -445,10 +450,16 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
})
}

/// Implement `WorldQuery` to use a struct as a parameter in a query
#[proc_macro_derive(WorldQuery, attributes(world_query))]
pub fn derive_world_query(input: TokenStream) -> TokenStream {
derive_world_query_impl(input)
/// Implement `WorldQueryData` to use a struct as a data parameter in a query
#[proc_macro_derive(WorldQueryData, attributes(world_query_data))]
pub fn derive_world_query_data(input: TokenStream) -> TokenStream {
derive_world_query_data_impl(input)
}

/// Implement `WorldQueryFilter` to use a struct as a filter parameter in a query
#[proc_macro_derive(WorldQueryFilter, attributes(world_query_filter))]
pub fn derive_world_query_filter(input: TokenStream) -> TokenStream {
derive_world_query_filter_impl(input)
}

/// Derive macro generating an impl of the trait `ScheduleLabel`.
Expand Down
189 changes: 189 additions & 0 deletions crates/bevy_ecs/macros/src/world_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use proc_macro2::Ident;
use quote::quote;
use syn::{Attribute, Fields, ImplGenerics, TypeGenerics, Visibility, WhereClause};

#[allow(clippy::too_many_arguments)]
pub(crate) fn item_struct(
path: &syn::Path,
fields: &Fields,
derive_macro_call: &proc_macro2::TokenStream,
struct_name: &Ident,
visibility: &Visibility,
item_struct_name: &Ident,
field_types: &Vec<proc_macro2::TokenStream>,
user_impl_generics_with_world: &ImplGenerics,
field_attrs: &Vec<Vec<Attribute>>,
field_visibilities: &Vec<Visibility>,
field_idents: &Vec<proc_macro2::TokenStream>,
user_ty_generics: &TypeGenerics,
user_ty_generics_with_world: &TypeGenerics,
user_where_clauses_with_world: Option<&WhereClause>,
) -> proc_macro2::TokenStream {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to avoid the sheer number of arguments here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quite possibly. I will have a go at factoring all the arguments into a small number of structs instead.

let item_attrs = quote!(
#[doc = "Automatically generated [`WorldQuery`] item type for [`"]
#[doc = stringify!(#struct_name)]
#[doc = "`], returned when iterating over query results."]
#[automatically_derived]
);

match fields {
syn::Fields::Named(_) => quote! {
#derive_macro_call
#item_attrs
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#(#field_attrs)* #field_visibilities #field_idents: <#field_types as #path::query::WorldQuery>::Item<'__w>,)*
}
},
syn::Fields::Unnamed(_) => quote! {
#derive_macro_call
#item_attrs
#[automatically_derived]
#visibility struct #item_struct_name #user_impl_generics_with_world #user_where_clauses_with_world(
#( #field_visibilities <#field_types as #path::query::WorldQuery>::Item<'__w>, )*
);
},
syn::Fields::Unit => quote! {
#item_attrs
#visibility type #item_struct_name #user_ty_generics_with_world = #struct_name #user_ty_generics;
},
}
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn world_query_impl(
path: &syn::Path,
struct_name: &Ident,
visibility: &Visibility,
item_struct_name: &Ident,
fetch_struct_name: &Ident,
field_types: &Vec<proc_macro2::TokenStream>,
user_impl_generics: &ImplGenerics,
user_impl_generics_with_world: &ImplGenerics,
field_idents: &Vec<proc_macro2::TokenStream>,
user_ty_generics: &TypeGenerics,
user_ty_generics_with_world: &TypeGenerics,
named_field_idents: &Vec<Ident>,
marker_name: &Ident,
state_struct_name: &Ident,
user_where_clauses: Option<&WhereClause>,
user_where_clauses_with_world: Option<&WhereClause>,
) -> proc_macro2::TokenStream {
quote! {
#[doc(hidden)]
#[doc = "Automatically generated internal [`WorldQuery`] fetch type for [`"]
#[doc = stringify!(#struct_name)]
#[doc = "`], used to define the world data accessed by this query."]
#[automatically_derived]
#visibility struct #fetch_struct_name #user_impl_generics_with_world #user_where_clauses_with_world {
#(#named_field_idents: <#field_types as #path::query::WorldQuery>::Fetch<'__w>,)*
#marker_name: &'__w (),
}

impl #user_impl_generics_with_world Clone for #fetch_struct_name #user_ty_generics_with_world
#user_where_clauses_with_world {
fn clone(&self) -> Self {
Self {
#(#named_field_idents: self.#named_field_idents.clone(),)*
#marker_name: &(),
}
}
}

// SAFETY: `update_component_access` and `update_archetype_component_access` are called on every field
unsafe impl #user_impl_generics #path::query::WorldQuery
for #struct_name #user_ty_generics #user_where_clauses {

type Item<'__w> = #item_struct_name #user_ty_generics_with_world;
type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world;
type State = #state_struct_name #user_ty_generics;

fn shrink<'__wlong: '__wshort, '__wshort>(
item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong>
) -> <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wshort> {
#item_struct_name {
#(
#field_idents: <#field_types>::shrink(item.#field_idents),
)*
}
}

unsafe fn init_fetch<'__w>(
_world: #path::world::unsafe_world_cell::UnsafeWorldCell<'__w>,
state: &Self::State,
_last_run: #path::component::Tick,
_this_run: #path::component::Tick,
) -> <Self as #path::query::WorldQuery>::Fetch<'__w> {
#fetch_struct_name {
#(#named_field_idents:
<#field_types>::init_fetch(
_world,
&state.#named_field_idents,
_last_run,
_this_run,
),
)*
#marker_name: &(),
}
}

const IS_DENSE: bool = true #(&& <#field_types>::IS_DENSE)*;

/// SAFETY: we call `set_archetype` for each member that implements `Fetch`
#[inline]
unsafe fn set_archetype<'__w>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State,
_archetype: &'__w #path::archetype::Archetype,
_table: &'__w #path::storage::Table
) {
#(<#field_types>::set_archetype(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _archetype, _table);)*
}

/// SAFETY: we call `set_table` for each member that implements `Fetch`
#[inline]
unsafe fn set_table<'__w>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_state: &Self::State,
_table: &'__w #path::storage::Table
) {
#(<#field_types>::set_table(&mut _fetch.#named_field_idents, &_state.#named_field_idents, _table);)*
}

/// SAFETY: we call `fetch` for each member that implements `Fetch`.
#[inline(always)]
unsafe fn fetch<'__w>(
_fetch: &mut <Self as #path::query::WorldQuery>::Fetch<'__w>,
_entity: #path::entity::Entity,
_table_row: #path::storage::TableRow,
) -> <Self as #path::query::WorldQuery>::Item<'__w> {
Self::Item {
#(#field_idents: <#field_types>::fetch(&mut _fetch.#named_field_idents, _entity, _table_row),)*
}
}

fn update_component_access(state: &Self::State, _access: &mut #path::query::FilteredAccess<#path::component::ComponentId>) {
#( <#field_types>::update_component_access(&state.#named_field_idents, _access); )*
}

fn update_archetype_component_access(
state: &Self::State,
_archetype: &#path::archetype::Archetype,
_access: &mut #path::query::Access<#path::archetype::ArchetypeComponentId>
) {
#(
<#field_types>::update_archetype_component_access(&state.#named_field_idents, _archetype, _access);
)*
}

fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
#state_struct_name {
#(#named_field_idents: <#field_types>::init_state(world),)*
}
}

fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(#path::component::ComponentId) -> bool) -> bool {
true #(&& <#field_types>::matches_component_set(&state.#named_field_idents, _set_contains_id))*
}
}
}
}
Loading