Simplified UI tree navigation without ghost_nodes (#17143)
# Objective There is a large performance regression in the UI systems in 0.15 because the `UiChildren` and `UiRootRootNodes` system params (even with `ghost_nodes` disabled) are really inefficient compared to regular queries and can trigger a heap allocation with large numbers of children. ## Solution Replace the `UiChildren` and `UiRootRootNodes` system params with simplified versions when the `ghost_nodes` feature is disabled. ## Testing yellow this PR, red main cargo run --example many_buttons --features "trace_tracy" --release `ui_stack_system` <img width="494" alt="stack" src="https://github.com/user-attachments/assets/4a09485f-0ded-4e54-bd47-ffbce869051a" /> `ui_layout_system` <img width="467" alt="unghosted" src="https://github.com/user-attachments/assets/9d906b20-66b6-4257-9eef-578de1827628" /> `update_clipping_system` <img width="454" alt="clipping" src="https://github.com/user-attachments/assets/320b50e8-1a1d-423a-95a0-42799ae72fc5" />
This commit is contained in:
parent
94b9fe384f
commit
17e3b850bd
@ -1,14 +1,17 @@
|
||||
//! This module contains [`GhostNode`] and utilities to flatten the UI hierarchy, traversing past ghost nodes.
|
||||
|
||||
use crate::Node;
|
||||
use bevy_ecs::{prelude::*, system::SystemParam};
|
||||
use bevy_hierarchy::{Children, HierarchyQueryExt, Parent};
|
||||
use bevy_hierarchy::{Children, Parent};
|
||||
use bevy_reflect::prelude::*;
|
||||
use bevy_render::view::Visibility;
|
||||
use bevy_transform::prelude::Transform;
|
||||
use core::marker::PhantomData;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::Node;
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
use bevy_hierarchy::HierarchyQueryExt;
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
use smallvec::SmallVec;
|
||||
|
||||
/// Marker component for entities that should be ignored within UI hierarchies.
|
||||
///
|
||||
@ -40,6 +43,7 @@ impl GhostNode {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
/// System param that allows iteration of all UI root nodes.
|
||||
///
|
||||
/// A UI root node is either a [`Node`] without a [`Parent`], or with only [`GhostNode`] ancestors.
|
||||
@ -51,6 +55,10 @@ pub struct UiRootNodes<'w, 's> {
|
||||
ui_children: UiChildren<'w, 's>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ghost_nodes"))]
|
||||
pub type UiRootNodes<'w, 's> = Query<'w, 's, Entity, (With<Node>, Without<Parent>)>;
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
impl<'w, 's> UiRootNodes<'w, 's> {
|
||||
pub fn iter(&'s self) -> impl Iterator<Item = Entity> + 's {
|
||||
self.root_node_query
|
||||
@ -62,6 +70,7 @@ impl<'w, 's> UiRootNodes<'w, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
/// System param that gives access to UI children utilities, skipping over [`GhostNode`].
|
||||
#[derive(SystemParam)]
|
||||
pub struct UiChildren<'w, 's> {
|
||||
@ -77,6 +86,16 @@ pub struct UiChildren<'w, 's> {
|
||||
parents_query: Query<'w, 's, &'static Parent>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ghost_nodes"))]
|
||||
/// System param that gives access to UI children utilities.
|
||||
#[derive(SystemParam)]
|
||||
pub struct UiChildren<'w, 's> {
|
||||
ui_children_query: Query<'w, 's, Option<&'static Children>, With<Node>>,
|
||||
changed_children_query: Query<'w, 's, Entity, Changed<Children>>,
|
||||
parents_query: Query<'w, 's, &'static Parent>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
impl<'w, 's> UiChildren<'w, 's> {
|
||||
/// Iterates the children of `entity`, skipping over [`GhostNode`].
|
||||
///
|
||||
@ -134,6 +153,40 @@ impl<'w, 's> UiChildren<'w, 's> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ghost_nodes"))]
|
||||
impl<'w, 's> UiChildren<'w, 's> {
|
||||
/// Iterates the children of `entity`.
|
||||
pub fn iter_ui_children(&'s self, entity: Entity) -> impl Iterator<Item = Entity> + 's {
|
||||
self.ui_children_query
|
||||
.get(entity)
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|children| children.as_ref())
|
||||
.unwrap_or(&[])
|
||||
.iter()
|
||||
.copied()
|
||||
}
|
||||
|
||||
/// Returns the UI parent of the provided entity.
|
||||
pub fn get_parent(&'s self, entity: Entity) -> Option<Entity> {
|
||||
self.parents_query
|
||||
.get(entity)
|
||||
.ok()
|
||||
.map(|parent| parent.entity())
|
||||
}
|
||||
|
||||
/// Given an entity in the UI hierarchy, check if its set of children has changed, e.g if children has been added/removed or if the order has changed.
|
||||
pub fn is_changed(&'s self, entity: Entity) -> bool {
|
||||
self.changed_children_query.contains(entity)
|
||||
}
|
||||
|
||||
/// Returns `true` if the given entity is either a [`Node`] or a [`GhostNode`].
|
||||
pub fn is_ui_node(&'s self, entity: Entity) -> bool {
|
||||
self.ui_children_query.contains(entity)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
pub struct UiChildrenIter<'w, 's> {
|
||||
stack: SmallVec<[Entity; 8]>,
|
||||
query: &'s Query<
|
||||
@ -144,6 +197,7 @@ pub struct UiChildrenIter<'w, 's> {
|
||||
>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "ghost_nodes")]
|
||||
impl<'w, 's> Iterator for UiChildrenIter<'w, 's> {
|
||||
type Item = Entity;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user