Reduce allocations in ui_layout_system (#15001)

# Objective

- Shave off some allocations from `ui_layout_system`.

## Solution

- Add a `Local` for allocation buffers.
This commit is contained in:
UkoeHB 2024-09-02 17:37:27 -05:00 committed by GitHub
parent 547b1c7a7a
commit 3fc02cb925
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,11 +1,11 @@
use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale}; use crate::{ContentSize, DefaultUiCamera, Node, Outline, Style, TargetCamera, UiScale};
use bevy_ecs::{ use bevy_ecs::{
change_detection::{DetectChanges, DetectChangesMut}, change_detection::{DetectChanges, DetectChangesMut},
entity::Entity, entity::{Entity, EntityHashMap, EntityHashSet},
event::EventReader, event::EventReader,
query::{With, Without}, query::{With, Without},
removal_detection::RemovedComponents, removal_detection::RemovedComponents,
system::{Query, Res, ResMut, SystemParam}, system::{Local, Query, Res, ResMut, SystemParam},
world::Ref, world::Ref,
}; };
use bevy_hierarchy::{Children, Parent}; use bevy_hierarchy::{Children, Parent};
@ -15,7 +15,6 @@ use bevy_render::camera::{Camera, NormalizedRenderTarget};
use bevy_text::{CosmicBuffer, TextPipeline}; use bevy_text::{CosmicBuffer, TextPipeline};
use bevy_transform::components::Transform; use bevy_transform::components::Transform;
use bevy_utils::tracing::warn; use bevy_utils::tracing::warn;
use bevy_utils::{HashMap, HashSet};
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged}; use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
use thiserror::Error; use thiserror::Error;
use ui_surface::UiSurface; use ui_surface::UiSurface;
@ -63,6 +62,7 @@ pub enum LayoutError {
TaffyError(#[from] taffy::TaffyError), TaffyError(#[from] taffy::TaffyError),
} }
#[doc(hidden)]
#[derive(SystemParam)] #[derive(SystemParam)]
pub struct UiLayoutSystemRemovedComponentParam<'w, 's> { pub struct UiLayoutSystemRemovedComponentParam<'w, 's> {
removed_cameras: RemovedComponents<'w, 's, Camera>, removed_cameras: RemovedComponents<'w, 's, Camera>,
@ -71,9 +71,25 @@ pub struct UiLayoutSystemRemovedComponentParam<'w, 's> {
removed_nodes: RemovedComponents<'w, 's, Node>, removed_nodes: RemovedComponents<'w, 's, Node>,
} }
#[doc(hidden)]
#[derive(Default)]
pub struct UiLayoutSystemBuffers {
interned_root_notes: Vec<Vec<Entity>>,
resized_windows: EntityHashSet,
camera_layout_info: EntityHashMap<CameraLayoutInfo>,
}
struct CameraLayoutInfo {
size: UVec2,
resized: bool,
scale_factor: f32,
root_nodes: Vec<Entity>,
}
/// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes. /// Updates the UI's layout tree, computes the new layout geometry and then updates the sizes and transforms of all the UI nodes.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn ui_layout_system( pub fn ui_layout_system(
mut buffers: Local<UiLayoutSystemBuffers>,
primary_window: Query<(Entity, &Window), With<PrimaryWindow>>, primary_window: Query<(Entity, &Window), With<PrimaryWindow>>,
cameras: Query<(Entity, &Camera)>, cameras: Query<(Entity, &Camera)>,
default_ui_camera: DefaultUiCamera, default_ui_camera: DefaultUiCamera,
@ -98,20 +114,20 @@ pub fn ui_layout_system(
#[cfg(feature = "bevy_text")] mut buffer_query: Query<&mut CosmicBuffer>, #[cfg(feature = "bevy_text")] mut buffer_query: Query<&mut CosmicBuffer>,
#[cfg(feature = "bevy_text")] mut text_pipeline: ResMut<TextPipeline>, #[cfg(feature = "bevy_text")] mut text_pipeline: ResMut<TextPipeline>,
) { ) {
struct CameraLayoutInfo { let UiLayoutSystemBuffers {
size: UVec2, interned_root_notes,
resized: bool, resized_windows,
scale_factor: f32, camera_layout_info,
root_nodes: Vec<Entity>, } = &mut *buffers;
}
let default_camera = default_ui_camera.get(); let default_camera = default_ui_camera.get();
let camera_with_default = |target_camera: Option<&TargetCamera>| { let camera_with_default = |target_camera: Option<&TargetCamera>| {
target_camera.map(TargetCamera::entity).or(default_camera) target_camera.map(TargetCamera::entity).or(default_camera)
}; };
let resized_windows: HashSet<Entity> = resize_events.read().map(|event| event.window).collect(); resized_windows.clear();
let calculate_camera_layout_info = |camera: &Camera| { resized_windows.extend(resize_events.read().map(|event| event.window));
let mut calculate_camera_layout_info = |camera: &Camera| {
let size = camera.physical_viewport_size().unwrap_or(UVec2::ZERO); let size = camera.physical_viewport_size().unwrap_or(UVec2::ZERO);
let scale_factor = camera.target_scaling_factor().unwrap_or(1.0); let scale_factor = camera.target_scaling_factor().unwrap_or(1.0);
let camera_target = camera let camera_target = camera
@ -124,12 +140,12 @@ pub fn ui_layout_system(
size, size,
resized, resized,
scale_factor: scale_factor * ui_scale.0, scale_factor: scale_factor * ui_scale.0,
root_nodes: Vec::new(), root_nodes: interned_root_notes.pop().unwrap_or_default(),
} }
}; };
// Precalculate the layout info for each camera, so we have fast access to it for each node // Precalculate the layout info for each camera, so we have fast access to it for each node
let mut camera_layout_info: HashMap<Entity, CameraLayoutInfo> = HashMap::new(); camera_layout_info.clear();
root_node_query.iter().for_each(|(entity,target_camera)|{ root_node_query.iter().for_each(|(entity,target_camera)|{
match camera_with_default(target_camera) { match camera_with_default(target_camera) {
Some(camera_entity) => { Some(camera_entity) => {
@ -232,11 +248,11 @@ pub fn ui_layout_system(
} }
}); });
for (camera_id, camera) in &camera_layout_info { for (camera_id, mut camera) in camera_layout_info.drain() {
let inverse_target_scale_factor = camera.scale_factor.recip(); let inverse_target_scale_factor = camera.scale_factor.recip();
ui_surface.compute_camera_layout( ui_surface.compute_camera_layout(
*camera_id, camera_id,
camera.size, camera.size,
#[cfg(feature = "bevy_text")] #[cfg(feature = "bevy_text")]
text_buffers, text_buffers,
@ -255,6 +271,9 @@ pub fn ui_layout_system(
Vec2::ZERO, Vec2::ZERO,
); );
} }
camera.root_nodes.clear();
interned_root_notes.push(camera.root_nodes);
} }
fn update_uinode_geometry_recursive( fn update_uinode_geometry_recursive(