From db19d2ee4705f18422dcb9dc4609474549333efb Mon Sep 17 00:00:00 2001 From: re0312 <45868716+re0312@users.noreply.github.com> Date: Mon, 8 Jul 2024 08:48:35 +0800 Subject: [PATCH] Optimize ui_layout_system (#14064) # Objective - Currently bevy's ui layout system could takes a long time. ## Solution - cache `default_ui_camera `entity to avoid repetitive lookup ## Performance cargo run --release --example many_buttons --features bevy/trace_tracy ![image](https://github.com/bevyengine/bevy/assets/45868716/f8eda0b0-343d-4379-847f-f1636c38e5ec) --- crates/bevy_ui/src/layout/mod.rs | 63 ++++++++++++++++---------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index e727faa7c9..886cef8b9c 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -104,10 +104,9 @@ pub fn ui_layout_system( root_nodes: Vec, } + let default_camera = default_ui_camera.get(); let camera_with_default = |target_camera: Option<&TargetCamera>| { - target_camera - .map(TargetCamera::entity) - .or(default_ui_camera.get()) + target_camera.map(TargetCamera::entity).or(default_camera) }; let resized_windows: HashSet = resize_events.read().map(|event| event.window).collect(); @@ -130,7 +129,7 @@ pub fn ui_layout_system( // Precalculate the layout info for each camera, so we have fast access to it for each node let mut camera_layout_info: HashMap = HashMap::new(); - for (entity, target_camera) in &root_node_query { + root_node_query.iter().for_each(|(entity,target_camera)|{ match camera_with_default(target_camera) { Some(camera_entity) => { let Ok((_, camera)) = cameras.get(camera_entity) else { @@ -138,7 +137,7 @@ pub fn ui_layout_system( "TargetCamera (of root UI node {entity:?}) is pointing to a camera {:?} which doesn't exist", camera_entity ); - continue; + return; }; let layout_info = camera_layout_info .entry(camera_entity) @@ -155,10 +154,10 @@ pub fn ui_layout_system( entity ); } - continue; } } - } + + }); // When a `ContentSize` component is removed from an entity, we need to remove the measure from the corresponding taffy node. for entity in removed_components.removed_content_sizes.read() { @@ -166,30 +165,32 @@ pub fn ui_layout_system( } // Sync Style and ContentSize to Taffy for all nodes - for (entity, style, content_size, target_camera) in style_query.iter_mut() { - if let Some(camera) = - camera_with_default(target_camera).and_then(|c| camera_layout_info.get(&c)) - { - if camera.resized - || !scale_factor_events.is_empty() - || ui_scale.is_changed() - || style.is_changed() - || content_size - .as_ref() - .map(|c| c.measure.is_some()) - .unwrap_or(false) + style_query + .iter_mut() + .for_each(|(entity, style, content_size, target_camera)| { + if let Some(camera) = + camera_with_default(target_camera).and_then(|c| camera_layout_info.get(&c)) { - let layout_context = LayoutContext::new( - camera.scale_factor, - [camera.size.x as f32, camera.size.y as f32].into(), - ); - let measure = content_size.and_then(|mut c| c.measure.take()); - ui_surface.upsert_node(&layout_context, entity, &style, measure); + if camera.resized + || !scale_factor_events.is_empty() + || ui_scale.is_changed() + || style.is_changed() + || content_size + .as_ref() + .map(|c| c.measure.is_some()) + .unwrap_or(false) + { + let layout_context = LayoutContext::new( + camera.scale_factor, + [camera.size.x as f32, camera.size.y as f32].into(), + ); + let measure = content_size.and_then(|mut c| c.measure.take()); + ui_surface.upsert_node(&layout_context, entity, &style, measure); + } + } else { + ui_surface.upsert_node(&LayoutContext::DEFAULT, entity, &Style::default(), None); } - } else { - ui_surface.upsert_node(&LayoutContext::DEFAULT, entity, &Style::default(), None); - } - } + }); scale_factor_events.clear(); // clean up removed cameras @@ -210,11 +211,11 @@ pub fn ui_layout_system( for entity in removed_components.removed_children.read() { ui_surface.try_remove_children(entity); } - for (entity, children) in &children_query { + children_query.iter().for_each(|(entity, children)| { if children.is_changed() { ui_surface.update_children(entity, &children); } - } + }); #[cfg(feature = "bevy_text")] let font_system = text_pipeline.font_system_mut();