Skip to content

Commit

Permalink
Implement WorldQuery for Parent and Children
Browse files Browse the repository at this point in the history
Uses the derived implementation based on `&T where T: Component`. This allows users to directly receive an `Entity` or a `&[Entity]` rather than needing to call methods on `Parent` or `Children` respectively.
  • Loading branch information
bushrat011899 committed Sep 5, 2024
1 parent a0f5ea0 commit 249cdab
Show file tree
Hide file tree
Showing 27 changed files with 250 additions and 77 deletions.
6 changes: 3 additions & 3 deletions crates/bevy_hierarchy/src/child_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1265,17 +1265,17 @@ mod tests {
.push_children(&[child])
.id();

let mut query = world.query::<&Children>();
let mut query = world.query::<Children>();
let children = query.get(&world, parent).unwrap();
assert_eq!(**children, [child]);
assert_eq!(children, [child]);
}

#[test]
fn push_children_does_not_insert_empty_children() {
let mut world = World::new();
let parent = world.spawn_empty().push_children(&[]).id();

let mut query = world.query::<&Children>();
let mut query = world.query::<Children>();
let children = query.get(&world, parent);
assert!(children.is_err());
}
Expand Down
100 changes: 98 additions & 2 deletions crates/bevy_hierarchy/src/components/children.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#[cfg(feature = "reflect")]
use bevy_ecs::reflect::{ReflectComponent, ReflectMapEntities};
use bevy_ecs::{
component::Component,
archetype::Archetype,
component::{Component, ComponentId, Components, Tick},
entity::{Entity, EntityMapper, MapEntities},
prelude::FromWorld,
world::World,
query::{FilteredAccess, QueryData, ReadFetch, ReadOnlyQueryData, WorldQuery},
storage::{Table, TableRow},
world::{unsafe_world_cell::UnsafeWorldCell, World},
};
use core::slice;
use smallvec::SmallVec;
Expand Down Expand Up @@ -162,3 +165,96 @@ impl<'a> IntoIterator for &'a Children {
self.0.iter()
}
}

#[allow(unsafe_code)]
/// SAFETY:
/// This implementation delegates to the existing implementation for &Children
unsafe impl WorldQuery for Children
where
Self: Component,
{
type Item<'w> = &'w [Entity];
type Fetch<'w> = ReadFetch<'w, Self>;
type State = ComponentId;

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

fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
<&Self as WorldQuery>::shrink_fetch(fetch)
}

#[inline]
unsafe fn init_fetch<'w>(
world: UnsafeWorldCell<'w>,
state: &Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::init_fetch(world, state, last_run, this_run) }
}

const IS_DENSE: bool = <&Self as WorldQuery>::IS_DENSE;

#[inline]
unsafe fn set_archetype<'w>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
archetype: &'w Archetype,
table: &'w Table,
) {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::set_archetype(fetch, state, archetype, table) }
}

#[inline]
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::set_table(fetch, state, table) }
}

#[inline(always)]
unsafe fn fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe {
<&Self as WorldQuery>::fetch(fetch, entity, table_row)
.0
.as_ref()
}
}

fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
<&Self as WorldQuery>::update_component_access(state, access);
}

fn init_state(world: &mut World) -> ComponentId {
<&Self as WorldQuery>::init_state(world)
}

fn get_state(components: &Components) -> Option<Self::State> {
<&Self as WorldQuery>::get_state(components)
}

fn matches_component_set(
state: &Self::State,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
<&Self as WorldQuery>::matches_component_set(state, set_contains_id)
}
}

#[allow(unsafe_code)]
/// SAFETY: `Self` is the same as `Self::ReadOnly`
unsafe impl QueryData for Children {
type ReadOnly = Self;
}

#[allow(unsafe_code)]
/// SAFETY: access is read only
unsafe impl ReadOnlyQueryData for Children {}
96 changes: 94 additions & 2 deletions crates/bevy_hierarchy/src/components/parent.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
#[cfg(feature = "reflect")]
use bevy_ecs::reflect::{ReflectComponent, ReflectMapEntities};
use bevy_ecs::{
component::Component,
archetype::Archetype,
component::{Component, ComponentId, Components, Tick},
entity::{Entity, EntityMapper, MapEntities},
query::{FilteredAccess, QueryData, ReadFetch, ReadOnlyQueryData, WorldQuery},
storage::{Table, TableRow},
traversal::Traversal,
world::{FromWorld, World},
world::{unsafe_world_cell::UnsafeWorldCell, FromWorld, World},
};
use std::ops::Deref;

Expand Down Expand Up @@ -81,3 +84,92 @@ impl Traversal for Parent {
Some(self.0)
}
}

#[allow(unsafe_code)]
/// SAFETY:
/// This implementation delegates to the existing implementation for &Parent
unsafe impl WorldQuery for Parent
where
Self: Component,
{
type Item<'w> = Entity;
type Fetch<'w> = ReadFetch<'w, Self>;
type State = ComponentId;

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

fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
<&Self as WorldQuery>::shrink_fetch(fetch)
}

#[inline]
unsafe fn init_fetch<'w>(
world: UnsafeWorldCell<'w>,
state: &Self::State,
last_run: Tick,
this_run: Tick,
) -> Self::Fetch<'w> {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::init_fetch(world, state, last_run, this_run) }
}

const IS_DENSE: bool = <&Self as WorldQuery>::IS_DENSE;

#[inline]
unsafe fn set_archetype<'w>(
fetch: &mut Self::Fetch<'w>,
state: &Self::State,
archetype: &'w Archetype,
table: &'w Table,
) {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::set_archetype(fetch, state, archetype, table) }
}

#[inline]
unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::set_table(fetch, state, table) }
}

#[inline(always)]
unsafe fn fetch<'w>(
fetch: &mut Self::Fetch<'w>,
entity: Entity,
table_row: TableRow,
) -> Self::Item<'w> {
// SAFETY: This implementation delegates to the existing implementation for &Self
unsafe { <&Self as WorldQuery>::fetch(fetch, entity, table_row).get() }
}

fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
<&Self as WorldQuery>::update_component_access(state, access);
}

fn init_state(world: &mut World) -> ComponentId {
<&Self as WorldQuery>::init_state(world)
}

fn get_state(components: &Components) -> Option<Self::State> {
<&Self as WorldQuery>::get_state(components)
}

fn matches_component_set(
state: &Self::State,
set_contains_id: &impl Fn(ComponentId) -> bool,
) -> bool {
<&Self as WorldQuery>::matches_component_set(state, set_contains_id)
}
}

#[allow(unsafe_code)]
/// SAFETY: `Self` is the same as `Self::ReadOnly`
unsafe impl QueryData for Parent {
type ReadOnly = Self;
}

#[allow(unsafe_code)]
/// SAFETY: access is read only
unsafe impl ReadOnlyQueryData for Parent {}
2 changes: 1 addition & 1 deletion crates/bevy_hierarchy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![deny(unsafe_code)]
#![doc(
html_logo_url = "https://bevyengine.org/assets/icon.png",
html_favicon_url = "https://bevyengine.org/assets/icon.png"
Expand Down
3 changes: 1 addition & 2 deletions crates/bevy_hierarchy/src/valid_parent_check_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,13 @@ impl<T> Default for ReportHierarchyIssue<T> {
/// (See B0004 explanation linked in warning message)
pub fn check_hierarchy_component_has_valid_parent<T: Component>(
parent_query: Query<
(Entity, &Parent, Option<&bevy_core::Name>),
(Entity, Parent, Option<&bevy_core::Name>),
(With<T>, Or<(Changed<Parent>, Added<T>)>),
>,
component_query: Query<(), With<T>>,
mut already_diagnosed: Local<HashSet<Entity>>,
) {
for (entity, parent, name) in &parent_query {
let parent = parent.get();
if !component_query.contains(parent) && !already_diagnosed.contains(&entity) {
already_diagnosed.insert(entity);
bevy_utils::tracing::warn!(
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_render/src/mesh/morph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ impl MeshMorphWeights {
///
/// Only direct children are updated, to fulfill the expectations of glTF spec.
pub fn inherit_weights(
morph_nodes: Query<(&Children, &MorphWeights), (Without<Handle<Mesh>>, Changed<MorphWeights>)>,
morph_nodes: Query<(Children, &MorphWeights), (Without<Handle<Mesh>>, Changed<MorphWeights>)>,
mut morph_primitives: Query<&mut MeshMorphWeights, With<Handle<Mesh>>>,
) {
for (children, parent_weights) in &morph_nodes {
Expand Down
8 changes: 4 additions & 4 deletions crates/bevy_render/src/view/visibility/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,19 +344,19 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(

fn visibility_propagate_system(
changed: Query<
(Entity, &Visibility, Option<&Parent>, Option<&Children>),
(Entity, &Visibility, Option<Parent>, Option<Children>),
(With<InheritedVisibility>, Changed<Visibility>),
>,
mut visibility_query: Query<(&Visibility, &mut InheritedVisibility)>,
children_query: Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
children_query: Query<Children, (With<Visibility>, With<InheritedVisibility>)>,
) {
for (entity, visibility, parent, children) in &changed {
let is_visible = match visibility {
Visibility::Visible => true,
Visibility::Hidden => false,
// fall back to true if no parent is found or parent lacks components
Visibility::Inherited => parent
.and_then(|p| visibility_query.get(p.get()).ok())
.and_then(|p| visibility_query.get(p).ok())
.map_or(true, |(_, x)| x.get()),
};
let (_, mut inherited_visibility) = visibility_query
Expand All @@ -382,7 +382,7 @@ fn propagate_recursive(
parent_is_visible: bool,
entity: Entity,
visibility_query: &mut Query<(&Visibility, &mut InheritedVisibility)>,
children_query: &Query<&Children, (With<Visibility>, With<InheritedVisibility>)>,
children_query: &Query<Children, (With<Visibility>, With<InheritedVisibility>)>,
// BLOCKED: https://github.com/rust-lang/rust/issues/31436
// We use a result here to use the `?` operator. Ideally we'd use a try block instead
) -> Result<(), ()> {
Expand Down
9 changes: 3 additions & 6 deletions crates/bevy_transform/src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ pub fn sync_simple_transforms(
/// Third party plugins should ensure that this is used in concert with [`sync_simple_transforms`].
pub fn propagate_transforms(
mut root_query: Query<
(Entity, &Children, Ref<Transform>, &mut GlobalTransform),
(Entity, Children, Ref<Transform>, &mut GlobalTransform),
Without<Parent>,
>,
mut orphaned: RemovedComponents<Parent>,
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<&Children>), With<Parent>>,
transform_query: Query<(Ref<Transform>, &mut GlobalTransform, Option<Children>), With<Parent>>,
parent_query: Query<(Entity, Ref<Parent>), With<GlobalTransform>>,
mut orphaned_entities: Local<Vec<Entity>>,
) {
Expand Down Expand Up @@ -110,10 +110,7 @@ pub fn propagate_transforms(
#[allow(unsafe_code)]
unsafe fn propagate_recursive(
parent: &GlobalTransform,
transform_query: &Query<
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
With<Parent>,
>,
transform_query: &Query<(Ref<Transform>, &mut GlobalTransform, Option<Children>), With<Parent>>,
parent_query: &Query<(Entity, Ref<Parent>), With<GlobalTransform>>,
entity: Entity,
mut changed: bool,
Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_ui/src/accessibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use bevy_render::{camera::CameraUpdateSystem, prelude::Camera};
use bevy_text::Text;
use bevy_transform::prelude::GlobalTransform;

fn calc_name(texts: &Query<&Text>, children: &Children) -> Option<Box<str>> {
fn calc_name(texts: &Query<&Text>, children: &[Entity]) -> Option<Box<str>> {
let mut name = None;
for child in children {
if let Ok(text) = texts.get(*child) {
Expand Down Expand Up @@ -59,7 +59,7 @@ fn calc_bounds(

fn button_changed(
mut commands: Commands,
mut query: Query<(Entity, &Children, Option<&mut AccessibilityNode>), Changed<Button>>,
mut query: Query<(Entity, Children, Option<&mut AccessibilityNode>), Changed<Button>>,
texts: Query<&Text>,
) {
for (entity, children, accessible) in &mut query {
Expand All @@ -86,7 +86,7 @@ fn button_changed(
fn image_changed(
mut commands: Commands,
mut query: Query<
(Entity, &Children, Option<&mut AccessibilityNode>),
(Entity, Children, Option<&mut AccessibilityNode>),
(Changed<UiImage>, Without<Button>),
>,
texts: Query<&Text>,
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ui/src/layout/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn ui_layout_system(
With<Node>,
>,
children_query: Query<(Entity, Ref<Children>), With<Node>>,
just_children_query: Query<&Children>,
just_children_query: Query<Children>,
mut removed_components: UiLayoutSystemRemovedComponentParam,
mut node_transform_query: Query<(
&mut Node,
Expand Down Expand Up @@ -293,7 +293,7 @@ pub fn ui_layout_system(
Option<&BorderRadius>,
Option<&Outline>,
)>,
children_query: &Query<&Children>,
children_query: &Query<Children>,
inverse_target_scale_factor: f32,
parent_size: Vec2,
mut absolute_location: Vec2,
Expand Down
Loading

0 comments on commit 249cdab

Please sign in to comment.