Skip to content
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

WorldQuery::Config for dynamic queries #8308

Closed
wants to merge 3 commits into from
Closed
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
7 changes: 5 additions & 2 deletions crates/bevy_ecs/macros/src/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
type Fetch<'__w> = #fetch_struct_name #user_ty_generics_with_world;
type ReadOnly = #read_only_struct_name #user_ty_generics;
type State = #state_struct_name #user_ty_generics;
type Config = ();

fn shrink<'__wlong: '__wshort, '__wshort>(
item: <#struct_name #user_ty_generics as #path::query::WorldQuery>::Item<'__wlong>
Expand Down Expand Up @@ -347,9 +348,11 @@ pub fn derive_world_query_impl(input: TokenStream) -> TokenStream {
)*
}

fn init_state(world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
fn init_state(_config: Self::Config, world: &mut #path::world::World) -> #state_struct_name #user_ty_generics {
#state_struct_name {
#(#named_field_idents: <#field_types>::init_state(world),)*
// TODO: instead of using `Default::default` for the config (and thus failing to compile on query types needing configuration like `Ptr<'_>`)
// we could have a tuple with configuration for each field, or generate a mirror struct with the same field names but storing the configuration values
#(#named_field_idents: <#field_types>::init_state(Default::default(), world),)*
}
}

Expand Down
10 changes: 8 additions & 2 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,10 @@ mod tests {
}
}

fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity> {
fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity>
where
F::Config: Default,
{
world
.query_filtered::<Entity, F>()
.iter(world)
Expand Down Expand Up @@ -990,7 +993,10 @@ mod tests {
}
}

fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity> {
fn get_filtered<F: ReadOnlyWorldQuery>(world: &mut World) -> Vec<Entity>
where
F::Config: Default,
{
world
.query_filtered::<Entity, F>()
.iter(world)
Expand Down
59 changes: 45 additions & 14 deletions crates/bevy_ecs/src/query/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,24 @@ pub unsafe trait WorldQuery {
/// constructing [`Self::Fetch`](crate::query::WorldQuery::Fetch).
type State: Send + Sync + Sized;

/// This associated type defines how a query can be configured at runtime.
///
/// [`QueryState::new_with_config`](crate::query::state::QueryState::new_with_config) uses this
/// associated type to construct [`QueryState`](crate::query::state::QueryState).
/// When this associated type implements the `Default` trait,
/// [`QueryState::new`](crate::query::state::QueryState::new) can be used instead.
///
/// In the case where no config is required, `()` is conventionally chosen
/// as the type for `Config`. This is the case for statically defined
/// queries, for types such as `&T`, since they can obtain a
/// [`ComponentId`] without any runtime information.
///
/// However, for dynamic queries, the [`ComponentId`] varies at runtime and
/// mut be additionally provided. By supplying `Config = ComponentId` we
/// can vary the component requested each time we construct a new query,
/// even for the same underlying Rust type.
Suficio marked this conversation as resolved.
Show resolved Hide resolved
type Config;
alice-i-cecile marked this conversation as resolved.
Show resolved Hide resolved

/// This function manually implements subtyping for the query items.
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort>;

Expand Down Expand Up @@ -434,7 +452,9 @@ pub unsafe trait WorldQuery {
);

/// Creates and initializes a [`State`](WorldQuery::State) for this [`WorldQuery`] type.
fn init_state(world: &mut World) -> Self::State;
///
/// [`WorldQuery::Config`] is used to pass runtime data for dynamic queries.
fn init_state(config: Self::Config, world: &mut World) -> Self::State;

/// Returns `true` if this query matches a set of components. Otherwise, returns `false`.
fn matches_component_set(
Expand Down Expand Up @@ -465,6 +485,7 @@ unsafe impl WorldQuery for Entity {
type Item<'w> = Entity;
type ReadOnly = Self;
type State = ();
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item
Expand Down Expand Up @@ -515,7 +536,7 @@ unsafe impl WorldQuery for Entity {
) {
}

fn init_state(_world: &mut World) {}
fn init_state(_config: (), _world: &mut World) {}

fn matches_component_set(
_state: &Self::State,
Expand All @@ -542,6 +563,7 @@ unsafe impl<T: Component> WorldQuery for &T {
type Item<'w> = &'w T;
type ReadOnly = Self;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(item: &'wlong T) -> &'wshort T {
item
Expand Down Expand Up @@ -652,7 +674,7 @@ unsafe impl<T: Component> WorldQuery for &T {
}
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand Down Expand Up @@ -688,6 +710,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
type Item<'w> = Ref<'w, T>;
type ReadOnly = Self;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(item: Ref<'wlong, T>) -> Ref<'wshort, T> {
item
Expand Down Expand Up @@ -814,7 +837,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
}
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand Down Expand Up @@ -850,6 +873,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
type Item<'w> = Mut<'w, T>;
type ReadOnly = &'__w T;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(item: Mut<'wlong, T>) -> Mut<'wshort, T> {
item
Expand Down Expand Up @@ -976,7 +1000,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
}
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand All @@ -1000,6 +1024,7 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
type Item<'w> = Option<T::Item<'w>>;
type ReadOnly = Option<T::ReadOnly>;
type State = T::State;
type Config = T::Config;

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item.map(T::shrink)
Expand Down Expand Up @@ -1081,8 +1106,8 @@ unsafe impl<T: WorldQuery> WorldQuery for Option<T> {
}
}

fn init_state(world: &mut World) -> T::State {
T::init_state(world)
fn init_state(config: Self::Config, world: &mut World) -> T::State {
T::init_state(config, world)
}

fn matches_component_set(
Expand All @@ -1106,6 +1131,7 @@ macro_rules! impl_tuple_fetch {
type Item<'w> = ($($name::Item<'w>,)*);
type ReadOnly = ($($name::ReadOnly,)*);
type State = ($($name::State,)*);
type Config = ($($name::Config,)*);

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
let ($($name,)*) = item;
Expand Down Expand Up @@ -1183,8 +1209,9 @@ macro_rules! impl_tuple_fetch {
}


fn init_state(_world: &mut World) -> Self::State {
($($name::init_state(_world),)*)
fn init_state(config: Self::Config, _world: &mut World) -> Self::State {
let ($($state,)*) = config;
($($name::init_state($state, _world),)*)
}

fn matches_component_set(state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
Expand Down Expand Up @@ -1216,6 +1243,7 @@ macro_rules! impl_anytuple_fetch {
type Item<'w> = ($(Option<$name::Item<'w>>,)*);
type ReadOnly = AnyOf<($($name::ReadOnly,)*)>;
type State = ($($name::State,)*);
type Config = ($($name::Config,)*);

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
let ($($name,)*) = item;
Expand Down Expand Up @@ -1313,8 +1341,9 @@ macro_rules! impl_anytuple_fetch {
)*
}

fn init_state(_world: &mut World) -> Self::State {
($($name::init_state(_world),)*)
fn init_state(config: Self::Config, _world: &mut World) -> Self::State {
let ($($state,)*) = config;
($($name::init_state($state, _world),)*)
}

fn matches_component_set(_state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
Expand Down Expand Up @@ -1342,6 +1371,7 @@ unsafe impl<Q: WorldQuery> WorldQuery for NopWorldQuery<Q> {
type Item<'w> = ();
type ReadOnly = Self;
type State = Q::State;
type Config = Q::Config;

fn shrink<'wlong: 'wshort, 'wshort>(_: ()) {}

Expand Down Expand Up @@ -1383,8 +1413,8 @@ unsafe impl<Q: WorldQuery> WorldQuery for NopWorldQuery<Q> {
) {
}

fn init_state(world: &mut World) -> Self::State {
Q::init_state(world)
fn init_state(config: Self::Config, world: &mut World) -> Self::State {
Q::init_state(config, world)
}

fn matches_component_set(
Expand All @@ -1404,6 +1434,7 @@ unsafe impl<T: ?Sized> WorldQuery for PhantomData<T> {
type Fetch<'a> = ();
type ReadOnly = Self;
type State = ();
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(_item: Self::Item<'wlong>) -> Self::Item<'wshort> {}

Expand Down Expand Up @@ -1450,7 +1481,7 @@ unsafe impl<T: ?Sized> WorldQuery for PhantomData<T> {
) {
}

fn init_state(_world: &mut World) -> Self::State {}
fn init_state(_config: Self::Config, _world: &mut World) -> Self::State {}

fn matches_component_set(
_state: &Self::State,
Expand Down
15 changes: 10 additions & 5 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
type Item<'w> = ();
type ReadOnly = Self;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}

Expand Down Expand Up @@ -97,7 +98,7 @@ unsafe impl<T: Component> WorldQuery for With<T> {
) {
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand Down Expand Up @@ -144,6 +145,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
type Item<'w> = ();
type ReadOnly = Self;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(_: Self::Item<'wlong>) -> Self::Item<'wshort> {}

Expand Down Expand Up @@ -194,7 +196,7 @@ unsafe impl<T: Component> WorldQuery for Without<T> {
) {
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand Down Expand Up @@ -258,6 +260,7 @@ macro_rules! impl_query_filter_tuple {
type Item<'w> = bool;
type ReadOnly = Or<($($filter::ReadOnly,)*)>;
type State = ($($filter::State,)*);
type Config = ($($filter::Config,)*);

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item
Expand Down Expand Up @@ -361,8 +364,9 @@ macro_rules! impl_query_filter_tuple {
$($filter::update_archetype_component_access($filter, archetype, access);)*
}

fn init_state(world: &mut World) -> Self::State {
($($filter::init_state(world),)*)
fn init_state(config: Self::Config, world: &mut World) -> Self::State {
let ($($state,)*) = config;
($($filter::init_state($state, world),)*)
}

fn matches_component_set(_state: &Self::State, _set_contains_id: &impl Fn(ComponentId) -> bool) -> bool {
Expand Down Expand Up @@ -406,6 +410,7 @@ macro_rules! impl_tick_filter {
type Item<'w> = bool;
type ReadOnly = Self;
type State = ComponentId;
type Config = ();

fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
item
Expand Down Expand Up @@ -532,7 +537,7 @@ macro_rules! impl_tick_filter {
}
}

fn init_state(world: &mut World) -> ComponentId {
fn init_state(_config: (), world: &mut World) -> ComponentId {
world.init_component::<T>()
}

Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ mod tests {
Q: ReadOnlyWorldQuery,
F: ReadOnlyWorldQuery,
F::ReadOnly: ArchetypeFilter,
Q::Config: Default,
F::Config: Default,
{
let mut query = world.query_filtered::<Q, F>();
let query_type = type_name::<QueryCombinationIter<Q, F, K>>();
Expand All @@ -129,6 +131,8 @@ mod tests {
Q: ReadOnlyWorldQuery,
F: ReadOnlyWorldQuery,
F::ReadOnly: ArchetypeFilter,
Q::Config: Default,
F::Config: Default,
{
let mut query = world.query_filtered::<Q, F>();
let query_type = type_name::<QueryState<Q, F>>();
Expand Down
27 changes: 23 additions & 4 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> std::fmt::Debug for QueryState<Q, F>
}
}

impl<Q: WorldQuery, F: ReadOnlyWorldQuery> FromWorld for QueryState<Q, F> {
impl<Q: WorldQuery, F: ReadOnlyWorldQuery> FromWorld for QueryState<Q, F>
where
<Q as WorldQuery>::Config: Default,
<F as WorldQuery>::Config: Default,
{
fn from_world(world: &mut World) -> Self {
world.query_filtered()
}
Expand Down Expand Up @@ -95,9 +99,24 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {

impl<Q: WorldQuery, F: ReadOnlyWorldQuery> 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::init_state(world);
let filter_state = F::init_state(world);
pub fn new(world: &mut World) -> Self
where
<Q as WorldQuery>::Config: Default,
<F as WorldQuery>::Config: Default,
{
let fetch_config = Default::default();
let filter_config = Default::default();
QueryState::new_with_config(world, fetch_config, filter_config)
}

/// Creates a new [`QueryState`] from a given [`World`], config, and inherits the result of `world.id()`.
pub fn new_with_config(
world: &mut World,
fetch_config: <Q as WorldQuery>::Config,
filter_config: <F as WorldQuery>::Config,
) -> Self {
let fetch_state = Q::init_state(fetch_config, world);
let filter_state = F::init_state(filter_config, world);

let mut component_access = FilteredAccess::default();
Q::update_component_access(&fetch_state, &mut component_access);
Expand Down
3 changes: 3 additions & 0 deletions crates/bevy_ecs/src/system/exclusive_system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub type ExclusiveSystemParamItem<'s, P> = <P as ExclusiveSystemParam>::Item<'s>

impl<'a, Q: WorldQuery + 'static, F: ReadOnlyWorldQuery + 'static> ExclusiveSystemParam
for &'a mut QueryState<Q, F>
where
<Q as WorldQuery>::Config: Default,
<F as WorldQuery>::Config: Default,
{
type State = QueryState<Q, F>;
type Item<'s> = &'s mut QueryState<Q, F>;
Expand Down
Loading