From 80e053ef5da5fb67c655a100bc8d1b4fdf1ea228 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 19 Jul 2023 13:55:18 +0100 Subject: [PATCH 01/60] `bevy_ui::render` Instead of a single list of `ExtractedUiNodes`, we have a separate list for each extraction function. Then we don't need to sort `ExtractedUiNodes` in `prepare_uinodes` as each sublist is already ordered. --- crates/bevy_ui/src/render/mod.rs | 73 +++++++++++++++++++++++++++----- 1 file changed, 63 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index f54ae28cb36fd..5ea331185511f 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -41,6 +41,7 @@ use bevy_utils::FloatOrd; use bevy_utils::HashMap; use bytemuck::{Pod, Zeroable}; use std::ops::Range; +use std::vec::Drain; pub mod node { pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; @@ -164,7 +165,20 @@ pub struct ExtractedUiNode { #[derive(Resource, Default)] pub struct ExtractedUiNodes { - pub uinodes: Vec, + pub uinodes: Vec>, +} + +impl ExtractedUiNodes { + pub fn get(&mut self) -> &mut Vec { + let empty_index = self.uinodes.iter().position(|uinodes| uinodes.is_empty()); + match empty_index { + Some(idx) => &mut self.uinodes[idx], + None => { + self.uinodes.push(vec![]); + self.uinodes.last_mut().unwrap() + } + } + } } pub fn extract_atlas_uinodes( @@ -187,6 +201,7 @@ pub fn extract_atlas_uinodes( >, >, ) { + let out = extracted_uinodes.get(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, visibility, clip, texture_atlas_handle, atlas_image)) = uinode_query.get(*entity) @@ -228,7 +243,7 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - extracted_uinodes.uinodes.push(ExtractedUiNode { + out.push(ExtractedUiNode { stack_index, transform: transform.compute_matrix(), color: color.0, @@ -276,6 +291,7 @@ pub fn extract_uinode_borders( >, parent_node_query: Extract>>, ) { + let out = extracted_uinodes.get(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -353,7 +369,7 @@ pub fn extract_uinode_borders( for edge in border_rects { if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - extracted_uinodes.uinodes.push(ExtractedUiNode { + out.push(ExtractedUiNode { stack_index, // This translates the uinode's transform to the center of the current border rectangle transform: transform * Mat4::from_translation(edge.center().extend(0.)), @@ -392,6 +408,7 @@ pub fn extract_uinodes( >, >, ) { + let out = extracted_uinodes.get(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, maybe_image, visibility, clip)) = uinode_query.get(*entity) @@ -411,7 +428,7 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.uinodes.push(ExtractedUiNode { + out.push(ExtractedUiNode { stack_index, transform: transform.compute_matrix(), color: color.0, @@ -520,6 +537,7 @@ pub fn extract_text_uinodes( )>, >, ) { + let out = extracted_uinodes.get(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -558,7 +576,7 @@ pub fn extract_text_uinodes( let mut rect = atlas.textures[atlas_info.glyph_index]; rect.min *= inverse_scale_factor; rect.max *= inverse_scale_factor; - extracted_uinodes.uinodes.push(ExtractedUiNode { + out.push(ExtractedUiNode { stack_index, transform: transform * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), @@ -627,10 +645,10 @@ pub fn prepare_uinodes( ) { ui_meta.vertices.clear(); - // sort by ui stack index, starting from the deepest node - extracted_uinodes - .uinodes - .sort_by_key(|node| node.stack_index); + // // sort by ui stack index, starting from the deepest node + // extracted_uinodes + // .uinodes + // .sort_by_key(|node| node.stack_index); let mut start = 0; let mut end = 0; @@ -642,7 +660,42 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - for extracted_uinode in extracted_uinodes.uinodes.drain(..) { + let mut nodeys: Vec<_> = + extracted_uinodes.uinodes.iter_mut() + .map(|uinodes| { + let mut d = uinodes.drain(..); + let first = d.next(); + (first, d) + }).collect(); + + fn next(nodeys: &mut Vec<(Option, Drain<'_, ExtractedUiNode>)>) -> Option { + let mut min_stack_index = usize::MAX; + let mut n = usize::MAX; + + for (i, (node, drainer)) in nodeys.iter_mut().enumerate() { + if let Some(node) = node { + if node.stack_index < min_stack_index { + n = i; + min_stack_index = node.stack_index; + } + } + } + + if n == usize::MAX { + return None; + } + + let (node, nodey) = &mut nodeys[n]; + let next = nodey.next(); + let out = node.take(); + *node = next; + out + + + } + + while let Some(extracted_uinode) = next(&mut nodeys) { + //for extracted_uinode in extracted_uinodes { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { From 977326c1cf2d82feacdc8933918703d2c6afa774 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 19 Jul 2023 19:38:41 +0100 Subject: [PATCH 02/60] Clean up and refactoring. --- crates/bevy_ui/src/render/mod.rs | 39 +++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 5ea331185511f..a5e92dc2be52a 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -41,7 +41,6 @@ use bevy_utils::FloatOrd; use bevy_utils::HashMap; use bytemuck::{Pod, Zeroable}; use std::ops::Range; -use std::vec::Drain; pub mod node { pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; @@ -645,11 +644,6 @@ pub fn prepare_uinodes( ) { ui_meta.vertices.clear(); - // // sort by ui stack index, starting from the deepest node - // extracted_uinodes - // .uinodes - // .sort_by_key(|node| node.stack_index); - let mut start = 0; let mut end = 0; let mut current_batch_image = DEFAULT_IMAGE_HANDLE.typed(); @@ -660,19 +654,21 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - let mut nodeys: Vec<_> = - extracted_uinodes.uinodes.iter_mut() + let mut drains: Vec<_> = extracted_uinodes + .uinodes + .iter_mut() .map(|uinodes| { let mut d = uinodes.drain(..); let first = d.next(); (first, d) - }).collect(); - - fn next(nodeys: &mut Vec<(Option, Drain<'_, ExtractedUiNode>)>) -> Option { + }) + .collect(); + + let mut next_extracted_node = move || { let mut min_stack_index = usize::MAX; let mut n = usize::MAX; - for (i, (node, drainer)) in nodeys.iter_mut().enumerate() { + for (i, (node, _)) in drains.iter_mut().enumerate() { if let Some(node) = node { if node.stack_index < min_stack_index { n = i; @@ -683,19 +679,16 @@ pub fn prepare_uinodes( if n == usize::MAX { return None; + } else { + let (current, drain) = &mut drains[n]; + let next = drain.next(); + let out = current.take(); + *current = next; + out } + }; - let (node, nodey) = &mut nodeys[n]; - let next = nodey.next(); - let out = node.take(); - *node = next; - out - - - } - - while let Some(extracted_uinode) = next(&mut nodeys) { - //for extracted_uinode in extracted_uinodes { + while let Some(extracted_uinode) = next_extracted_node() { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { From 656c7a23eb6e35a9e1d4e96f0b31061e470177ad Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 19 Jul 2023 22:07:18 +0100 Subject: [PATCH 03/60] removed unneeded return --- crates/bevy_ui/src/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index a5e92dc2be52a..0917055972738 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -678,7 +678,7 @@ pub fn prepare_uinodes( } if n == usize::MAX { - return None; + None } else { let (current, drain) = &mut drains[n]; let next = drain.next(); From 658feecb9de527719bcfe07309cdfe330e762986 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 19 Jul 2023 23:13:27 +0100 Subject: [PATCH 04/60] Renamed `get` method to `next_buffer` --- crates/bevy_ui/src/render/mod.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 0917055972738..64522441c474d 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -168,7 +168,7 @@ pub struct ExtractedUiNodes { } impl ExtractedUiNodes { - pub fn get(&mut self) -> &mut Vec { + pub fn next_buffer(&mut self) -> &mut Vec { let empty_index = self.uinodes.iter().position(|uinodes| uinodes.is_empty()); match empty_index { Some(idx) => &mut self.uinodes[idx], @@ -200,7 +200,7 @@ pub fn extract_atlas_uinodes( >, >, ) { - let out = extracted_uinodes.get(); + let out = extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, visibility, clip, texture_atlas_handle, atlas_image)) = uinode_query.get(*entity) @@ -290,7 +290,7 @@ pub fn extract_uinode_borders( >, parent_node_query: Extract>>, ) { - let out = extracted_uinodes.get(); + let out = extracted_uinodes.next_buffer(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -407,7 +407,7 @@ pub fn extract_uinodes( >, >, ) { - let out = extracted_uinodes.get(); + let out = extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, maybe_image, visibility, clip)) = uinode_query.get(*entity) @@ -536,7 +536,7 @@ pub fn extract_text_uinodes( )>, >, ) { - let out = extracted_uinodes.get(); + let out = extracted_uinodes.next_buffer(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() From 21f7f2c5bc679bf327127c94acb5c18fa0e06d2d Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 19 Jul 2023 23:18:45 +0100 Subject: [PATCH 05/60] Added doc comment --- crates/bevy_ui/src/render/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 64522441c474d..2fcb166399bb6 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -168,6 +168,7 @@ pub struct ExtractedUiNodes { } impl ExtractedUiNodes { + /// Retrieves the next empty `ExtractedUiNode` buffer. If none exists, creates one before returning it. pub fn next_buffer(&mut self) -> &mut Vec { let empty_index = self.uinodes.iter().position(|uinodes| uinodes.is_empty()); match empty_index { From 3e71e45f0d59cbbf313b4ffa4ad81d11318c309a Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 14:39:28 +0100 Subject: [PATCH 06/60] Add iterator for ExtractedUiNodes --- crates/bevy_ui/src/render/mod.rs | 136 +++++++++++++++++++++++-------- 1 file changed, 101 insertions(+), 35 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 2fcb166399bb6..6eaede92daa5a 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -41,6 +41,7 @@ use bevy_utils::FloatOrd; use bevy_utils::HashMap; use bytemuck::{Pod, Zeroable}; use std::ops::Range; +use std::vec::Drain; pub mod node { pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; @@ -179,8 +180,73 @@ impl ExtractedUiNodes { } } } + + fn drain<'a>(&'a mut self) -> ExtractedUiNodesDrainingIterator<'a> { + let mut drains: Vec<_> = self + .uinodes + .iter_mut() + .map(|uinodes| uinodes.drain(..)) + .collect(); + let next_uinodes = drains.iter_mut().map(|uinodes| uinodes.next()).collect(); + ExtractedUiNodesDrainingIterator { + next_uinodes, + uinodes: drains + } + } +} + +struct ExtractedUiNodesDrainingIterator<'a> { + next_uinodes: Vec>, + uinodes: Vec>, +} + +impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { + type Item=ExtractedUiNode; + + fn next(&mut self) -> Option { + let mut min_stack_index = usize::MAX; + let mut n = usize::MAX; + for (i, node) in self.next_uinodes.iter().enumerate() { + if let Some(node) = node { + if node.stack_index < min_stack_index { + n = i; + min_stack_index = node.stack_index; + } + } + } + + // let n = + // self.next_uinodes.iter().enumerate() + // .min_by(|(_, a), (_, b)| { + // let c = a.map_or(usize::MAX, |a| a.stack_index); + // let d = b.map_or(usize::MAX, |b| b.stack_index); + // c.cmp(&d) + // }) + // .map(|(idx, _)| idx); + + if n == usize::MAX { + None + } else { + let drain = &mut self.uinodes[n]; + let current = &mut self.next_uinodes[n]; + let next = drain.next(); + let out = current.take(); + *current = next; + out + } + + // n.and_then(|n| { + // let drain = &mut self.uinodes[n]; + // let current = self.next_uinodes[n]; + // let next = drain.next(); + // let out = current.take(); + // *current = next; + // out + // }) + } } + pub fn extract_atlas_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, @@ -655,41 +721,41 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - let mut drains: Vec<_> = extracted_uinodes - .uinodes - .iter_mut() - .map(|uinodes| { - let mut d = uinodes.drain(..); - let first = d.next(); - (first, d) - }) - .collect(); - - let mut next_extracted_node = move || { - let mut min_stack_index = usize::MAX; - let mut n = usize::MAX; - - for (i, (node, _)) in drains.iter_mut().enumerate() { - if let Some(node) = node { - if node.stack_index < min_stack_index { - n = i; - min_stack_index = node.stack_index; - } - } - } - - if n == usize::MAX { - None - } else { - let (current, drain) = &mut drains[n]; - let next = drain.next(); - let out = current.take(); - *current = next; - out - } - }; - - while let Some(extracted_uinode) = next_extracted_node() { + // let mut drains: Vec<_> = extracted_uinodes + // .uinodes + // .iter_mut() + // .map(|uinodes| { + // let mut d = uinodes.drain(..); + // let first = d.next(); + // (first, d) + // }) + // .collect(); + + // let mut next_extracted_node = move || { + // let mut min_stack_index = usize::MAX; + // let mut n = usize::MAX; + + // for (i, (node, _)) in drains.iter_mut().enumerate() { + // if let Some(node) = node { + // if node.stack_index < min_stack_index { + // n = i; + // min_stack_index = node.stack_index; + // } + // } + // } + + // if n == usize::MAX { + // None + // } else { + // let (current, drain) = &mut drains[n]; + // let next = drain.next(); + // let out = current.take(); + // *current = next; + // out + // } + // }; + + for extracted_uinode in extracted_uinodes.drain() { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { From df19fddcec58a1fa15c9f5e8c0f51446345e2876 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:31:11 +0100 Subject: [PATCH 07/60] clean up --- crates/bevy_ui/src/render/mod.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 6eaede92daa5a..30d9e1d81f8b8 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -214,15 +214,6 @@ impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { } } } - - // let n = - // self.next_uinodes.iter().enumerate() - // .min_by(|(_, a), (_, b)| { - // let c = a.map_or(usize::MAX, |a| a.stack_index); - // let d = b.map_or(usize::MAX, |b| b.stack_index); - // c.cmp(&d) - // }) - // .map(|(idx, _)| idx); if n == usize::MAX { None @@ -234,15 +225,6 @@ impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { *current = next; out } - - // n.and_then(|n| { - // let drain = &mut self.uinodes[n]; - // let current = self.next_uinodes[n]; - // let next = drain.next(); - // let out = current.take(); - // *current = next; - // out - // }) } } From 6008012f4cc574257f1e9cc95bf91d4142f07c1c Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:32:48 +0100 Subject: [PATCH 08/60] Delete commented out code --- crates/bevy_ui/src/render/mod.rs | 34 -------------------------------- 1 file changed, 34 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 30d9e1d81f8b8..24e742c4766b6 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -703,40 +703,6 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - // let mut drains: Vec<_> = extracted_uinodes - // .uinodes - // .iter_mut() - // .map(|uinodes| { - // let mut d = uinodes.drain(..); - // let first = d.next(); - // (first, d) - // }) - // .collect(); - - // let mut next_extracted_node = move || { - // let mut min_stack_index = usize::MAX; - // let mut n = usize::MAX; - - // for (i, (node, _)) in drains.iter_mut().enumerate() { - // if let Some(node) = node { - // if node.stack_index < min_stack_index { - // n = i; - // min_stack_index = node.stack_index; - // } - // } - // } - - // if n == usize::MAX { - // None - // } else { - // let (current, drain) = &mut drains[n]; - // let next = drain.next(); - // let out = current.take(); - // *current = next; - // out - // } - // }; - for extracted_uinode in extracted_uinodes.drain() { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { From dc6b14fc1595f183cb089fce889d61a80e8377a7 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:33:11 +0100 Subject: [PATCH 09/60] cargo fmt --- crates/bevy_ui/src/render/mod.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 24e742c4766b6..aa5dbaa6b83c7 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -188,9 +188,9 @@ impl ExtractedUiNodes { .map(|uinodes| uinodes.drain(..)) .collect(); let next_uinodes = drains.iter_mut().map(|uinodes| uinodes.next()).collect(); - ExtractedUiNodesDrainingIterator { - next_uinodes, - uinodes: drains + ExtractedUiNodesDrainingIterator { + next_uinodes, + uinodes: drains, } } } @@ -200,8 +200,8 @@ struct ExtractedUiNodesDrainingIterator<'a> { uinodes: Vec>, } -impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { - type Item=ExtractedUiNode; +impl<'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { + type Item = ExtractedUiNode; fn next(&mut self) -> Option { let mut min_stack_index = usize::MAX; @@ -214,7 +214,7 @@ impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { } } } - + if n == usize::MAX { None } else { @@ -228,7 +228,6 @@ impl <'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { } } - pub fn extract_atlas_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, From 4550b6d8d826d2c452b78e0e24981483bafcd143 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:55:24 +0100 Subject: [PATCH 10/60] renamed `out` variables in extraction functions to `output_buffer` --- crates/bevy_ui/src/render/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index aa5dbaa6b83c7..618b1a496d092 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -248,7 +248,7 @@ pub fn extract_atlas_uinodes( >, >, ) { - let out = extracted_uinodes.next_buffer(); + let output_buffer= extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, visibility, clip, texture_atlas_handle, atlas_image)) = uinode_query.get(*entity) @@ -338,7 +338,7 @@ pub fn extract_uinode_borders( >, parent_node_query: Extract>>, ) { - let out = extracted_uinodes.next_buffer(); + let output_buffer= extracted_uinodes.next_buffer(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -416,7 +416,7 @@ pub fn extract_uinode_borders( for edge in border_rects { if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - out.push(ExtractedUiNode { + output_buffer.push(ExtractedUiNode { stack_index, // This translates the uinode's transform to the center of the current border rectangle transform: transform * Mat4::from_translation(edge.center().extend(0.)), @@ -455,7 +455,7 @@ pub fn extract_uinodes( >, >, ) { - let out = extracted_uinodes.next_buffer(); + let output_buffer= extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, maybe_image, visibility, clip)) = uinode_query.get(*entity) @@ -475,7 +475,7 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - out.push(ExtractedUiNode { + output_buffer.push(ExtractedUiNode { stack_index, transform: transform.compute_matrix(), color: color.0, @@ -584,7 +584,7 @@ pub fn extract_text_uinodes( )>, >, ) { - let out = extracted_uinodes.next_buffer(); + let output_buffer= extracted_uinodes.next_buffer(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -623,7 +623,7 @@ pub fn extract_text_uinodes( let mut rect = atlas.textures[atlas_info.glyph_index]; rect.min *= inverse_scale_factor; rect.max *= inverse_scale_factor; - out.push(ExtractedUiNode { + output_buffer.push(ExtractedUiNode { stack_index, transform: transform * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), From 23c2a05b7fd50d16dc8e628274fd60a0cc363656 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:56:20 +0100 Subject: [PATCH 11/60] cargo fmt --- crates/bevy_ui/src/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 618b1a496d092..5487fe61f5d49 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -290,7 +290,7 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - out.push(ExtractedUiNode { + output_buffer.push(ExtractedUiNode { stack_index, transform: transform.compute_matrix(), color: color.0, From 9362b30a925063f32aaa4aab07e25e19f4ee5cef Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 17:56:41 +0100 Subject: [PATCH 12/60] cargo fmt --all --- crates/bevy_ui/src/render/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 5487fe61f5d49..b64ea390aa1fa 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -248,7 +248,7 @@ pub fn extract_atlas_uinodes( >, >, ) { - let output_buffer= extracted_uinodes.next_buffer(); + let output_buffer = extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, visibility, clip, texture_atlas_handle, atlas_image)) = uinode_query.get(*entity) @@ -338,7 +338,7 @@ pub fn extract_uinode_borders( >, parent_node_query: Extract>>, ) { - let output_buffer= extracted_uinodes.next_buffer(); + let output_buffer = extracted_uinodes.next_buffer(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -455,7 +455,7 @@ pub fn extract_uinodes( >, >, ) { - let output_buffer= extracted_uinodes.next_buffer(); + let output_buffer = extracted_uinodes.next_buffer(); for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { if let Ok((uinode, transform, color, maybe_image, visibility, clip)) = uinode_query.get(*entity) @@ -584,7 +584,7 @@ pub fn extract_text_uinodes( )>, >, ) { - let output_buffer= extracted_uinodes.next_buffer(); + let output_buffer = extracted_uinodes.next_buffer(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() From fa41464b77255ede3bb985f5647d61972a2f8db8 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 23:26:54 +0100 Subject: [PATCH 13/60] use `std::mem::replace` in next function --- crates/bevy_ui/src/render/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index b64ea390aa1fa..cfd9da977a046 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -185,6 +185,7 @@ impl ExtractedUiNodes { let mut drains: Vec<_> = self .uinodes .iter_mut() + .filter(|uinodes| !uinodes.is_empty()) .map(|uinodes| uinodes.drain(..)) .collect(); let next_uinodes = drains.iter_mut().map(|uinodes| uinodes.next()).collect(); @@ -218,12 +219,7 @@ impl<'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { if n == usize::MAX { None } else { - let drain = &mut self.uinodes[n]; - let current = &mut self.next_uinodes[n]; - let next = drain.next(); - let out = current.take(); - *current = next; - out + std::mem::replace(&mut self.next_uinodes[n], self.uinodes[n].next()) } } } From 6d00a1f4b7f81cf26f5cbfdcc6216ee586996563 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 20 Jul 2023 23:36:04 +0100 Subject: [PATCH 14/60] switch if condition branches --- crates/bevy_ui/src/render/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index cfd9da977a046..2973f261851ff 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -216,10 +216,10 @@ impl<'a> Iterator for ExtractedUiNodesDrainingIterator<'a> { } } - if n == usize::MAX { - None - } else { + if n < usize::MAX { std::mem::replace(&mut self.next_uinodes[n], self.uinodes[n].next()) + } else { + None } } } From 4ac35a945f15c0f4daee968f8e40053c53b7f699 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 11:02:00 +0100 Subject: [PATCH 15/60] `bevy_ui::render` Instead of sorting `ExtractedUiNodes`, add a lookup index. Then in prepare_uinodes sort the indices by `stack_index` then use the sorted indices to retrieve each `ExtractedUiNode` in the correct order. * Added a field `indices` to `ExtractedUiNodes`. * Added `ExtractedIndex` type. --- crates/bevy_ui/src/render/mod.rs | 132 +++++++++++++++++++------------ 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 328ba7b1a6cba..5ddf662f06afa 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -154,7 +154,6 @@ fn get_ui_graph(render_app: &mut App) -> RenderGraph { } pub struct ExtractedUiNode { - pub stack_index: usize, pub transform: Mat4, pub color: Color, pub rect: Rect, @@ -167,9 +166,25 @@ pub struct ExtractedUiNode { #[derive(Resource, Default)] pub struct ExtractedUiNodes { + pub indices: Vec, pub uinodes: Vec, } +pub struct ExtractedIndex { + stack_index: u32, + uinode_index: u32, +} + +impl ExtractedUiNodes { + pub fn push(&mut self, stack_index: usize, item: ExtractedUiNode) { + self.indices.push(ExtractedIndex { + stack_index: stack_index as u32, + uinode_index: self.uinodes.len() as u32, + }); + self.uinodes.push(item); + } +} + pub fn extract_atlas_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, @@ -231,17 +246,19 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - extracted_uinodes.uinodes.push(ExtractedUiNode { + extracted_uinodes.push( stack_index, - transform: transform.compute_matrix(), - color: color.0, - rect: atlas_rect, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: Some(atlas_size), - flip_x: atlas_image.flip_x, - flip_y: atlas_image.flip_y, - }); + ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: atlas_rect, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: Some(atlas_size), + flip_x: atlas_image.flip_x, + flip_y: atlas_image.flip_y, + }, + ); } } } @@ -356,21 +373,23 @@ pub fn extract_uinode_borders( for edge in border_rects { if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - extracted_uinodes.uinodes.push(ExtractedUiNode { + extracted_uinodes.push( stack_index, - // This translates the uinode's transform to the center of the current border rectangle - transform: transform * Mat4::from_translation(edge.center().extend(0.)), - color: border_color.0, - rect: Rect { - max: edge.size(), - ..Default::default() + ExtractedUiNode { + // This translates the uinode's transform to the center of the current border rectangle + transform: transform * Mat4::from_translation(edge.center().extend(0.)), + color: border_color.0, + rect: Rect { + max: edge.size(), + ..Default::default() + }, + image: image.clone_weak(), + atlas_size: None, + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, }, - image: image.clone_weak(), - atlas_size: None, - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); + ); } } } @@ -414,20 +433,22 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.uinodes.push(ExtractedUiNode { + extracted_uinodes.push( stack_index, - transform: transform.compute_matrix(), - color: color.0, - rect: Rect { - min: Vec2::ZERO, - max: uinode.calculated_size, + ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: Rect { + min: Vec2::ZERO, + max: uinode.calculated_size, + }, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: None, + flip_x, + flip_y, }, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: None, - flip_x, - flip_y, - }); + ); }; } } @@ -561,18 +582,20 @@ pub fn extract_text_uinodes( let mut rect = atlas.textures[atlas_info.glyph_index]; rect.min *= inverse_scale_factor; rect.max *= inverse_scale_factor; - extracted_uinodes.uinodes.push(ExtractedUiNode { + extracted_uinodes.push( stack_index, - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); + ExtractedUiNode { + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + }, + ); } } } @@ -631,9 +654,13 @@ pub fn prepare_uinodes( ui_meta.vertices.clear(); // sort by ui stack index, starting from the deepest node + // extracted_uinodes + // .uinodes + // .sort_by_key(|node| node.stack_index); + extracted_uinodes - .uinodes - .sort_by_key(|node| node.stack_index); + .indices + .sort_by_key(|extracted_index| extracted_index.stack_index); let mut start = 0; let mut end = 0; @@ -645,7 +672,9 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - for extracted_uinode in extracted_uinodes.uinodes.drain(..) { + //for extracted_uinode in extracted_uinodes.uinodes.drain(..) { + for index in &extracted_uinodes.indices { + let extracted_uinode = &extracted_uinodes.uinodes[index.uinode_index as usize]; let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { @@ -784,6 +813,9 @@ pub fn prepare_uinodes( } ui_meta.vertices.write_buffer(&render_device, &render_queue); + + extracted_uinodes.uinodes.clear(); + extracted_uinodes.indices.clear(); } #[derive(Resource, Default)] From cbbd2568d6943fb63d6b39e783c2b61e9b11a0a9 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 11:38:22 +0100 Subject: [PATCH 16/60] `UiStackIndex` * New component, holds the uinode entity's stack index. Updated in `ui_stack_system after the `UiStack` is generated. `bevy_ui::node_bundles` * Added the `UiStackIndex` to all UI node bundles. `bevy_ui::render` * Extract stack indices from the `UiNode` component. --- crates/bevy_ui/src/node_bundles.rs | 10 +- crates/bevy_ui/src/render/mod.rs | 398 ++++++++++++++--------------- crates/bevy_ui/src/stack.rs | 8 +- crates/bevy_ui/src/ui_node.rs | 3 + 4 files changed, 217 insertions(+), 202 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index a8c82a4828893..308f90e57a7be 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -5,7 +5,7 @@ use crate::widget::TextFlags; use crate::{ widget::{Button, UiImageSize}, BackgroundColor, BorderColor, ContentSize, FocusPolicy, Interaction, Node, Style, UiImage, - UiTextureAtlasImage, ZIndex, + UiStackIndex, UiTextureAtlasImage, ZIndex, }; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; @@ -50,6 +50,7 @@ pub struct NodeBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + pub stack_index: UiStackIndex, } impl Default for NodeBundle { @@ -66,6 +67,7 @@ impl Default for NodeBundle { visibility: Default::default(), computed_visibility: Default::default(), z_index: Default::default(), + stack_index: Default::default(), } } } @@ -111,6 +113,7 @@ pub struct ImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + pub stack_index: UiStackIndex, } /// A UI node that is a texture atlas sprite @@ -156,6 +159,7 @@ pub struct AtlasImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + pub stack_index: UiStackIndex, } #[cfg(feature = "bevy_text")] @@ -193,6 +197,7 @@ pub struct TextBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, } @@ -215,6 +220,7 @@ impl Default for TextBundle { visibility: Default::default(), computed_visibility: Default::default(), z_index: Default::default(), + stack_index: Default::default(), } } } @@ -305,6 +311,7 @@ pub struct ButtonBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + pub stack_index: UiStackIndex, } impl Default for ButtonBundle { @@ -323,6 +330,7 @@ impl Default for ButtonBundle { visibility: Default::default(), computed_visibility: Default::default(), z_index: Default::default(), + stack_index: Default::default(), } } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 328ba7b1a6cba..26300f1d53fa2 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -9,6 +9,7 @@ use bevy_window::{PrimaryWindow, Window}; pub use pipeline::*; pub use render_pass::*; +use crate::UiStackIndex; use crate::{ prelude::UiCameraConfig, BackgroundColor, BorderColor, CalculatedClip, ContentSize, Node, Style, UiImage, UiScale, UiStack, UiTextureAtlasImage, Val, @@ -174,10 +175,10 @@ pub fn extract_atlas_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, texture_atlases: Extract>>, - ui_stack: Extract>, uinode_query: Extract< Query< ( + &UiStackIndex, &Node, &GlobalTransform, &BackgroundColor, @@ -190,59 +191,65 @@ pub fn extract_atlas_uinodes( >, >, ) { - for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((uinode, transform, color, visibility, clip, texture_atlas_handle, atlas_image)) = - uinode_query.get(*entity) - { - // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { - continue; - } - - let (mut atlas_rect, mut atlas_size, image) = - if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) { - let atlas_rect = *texture_atlas - .textures - .get(atlas_image.index) - .unwrap_or_else(|| { - panic!( - "Atlas index {:?} does not exist for texture atlas handle {:?}.", - atlas_image.index, - texture_atlas_handle.id(), - ) - }); - ( - atlas_rect, - texture_atlas.size, - texture_atlas.texture.clone(), - ) - } else { - // Atlas not present in assets resource (should this warn the user?) - continue; - }; + for ( + stack_index, + uinode, + transform, + color, + visibility, + clip, + texture_atlas_handle, + atlas_image, + ) in uinode_query.iter() + { + // Skip invisible and completely transparent nodes + if !visibility.is_visible() || color.0.a() == 0.0 { + continue; + } - // Skip loading images - if !images.contains(&image) { + let (mut atlas_rect, mut atlas_size, image) = + if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) { + let atlas_rect = *texture_atlas + .textures + .get(atlas_image.index) + .unwrap_or_else(|| { + panic!( + "Atlas index {:?} does not exist for texture atlas handle {:?}.", + atlas_image.index, + texture_atlas_handle.id(), + ) + }); + ( + atlas_rect, + texture_atlas.size, + texture_atlas.texture.clone(), + ) + } else { + // Atlas not present in assets resource (should this warn the user?) continue; - } - - let scale = uinode.size() / atlas_rect.size(); - atlas_rect.min *= scale; - atlas_rect.max *= scale; - atlas_size *= scale; + }; - extracted_uinodes.uinodes.push(ExtractedUiNode { - stack_index, - transform: transform.compute_matrix(), - color: color.0, - rect: atlas_rect, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: Some(atlas_size), - flip_x: atlas_image.flip_x, - flip_y: atlas_image.flip_y, - }); + // Skip loading images + if !images.contains(&image) { + continue; } + + let scale = uinode.size() / atlas_rect.size(); + atlas_rect.min *= scale; + atlas_rect.max *= scale; + atlas_size *= scale; + + extracted_uinodes.uinodes.push(ExtractedUiNode { + stack_index: stack_index.0, + transform: transform.compute_matrix(), + color: color.0, + rect: atlas_rect, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: Some(atlas_size), + flip_x: atlas_image.flip_x, + flip_y: atlas_image.flip_y, + }); } } @@ -266,6 +273,7 @@ pub fn extract_uinode_borders( uinode_query: Extract< Query< ( + &UiStackIndex, &Node, &GlobalTransform, &Style, @@ -289,89 +297,81 @@ pub fn extract_uinode_borders( // so we have to divide by `UiScale` to get the size of the UI viewport. / ui_scale.scale as f32; - for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((node, global_transform, style, border_color, parent, visibility, clip)) = - uinode_query.get(*entity) + for (stack_index, node, global_transform, style, border_color, parent, visibility, clip) in + uinode_query.iter() + { + // Skip invisible borders + if !visibility.is_visible() + || border_color.0.a() == 0.0 + || node.size().x <= 0. + || node.size().y <= 0. { - // Skip invisible borders - if !visibility.is_visible() - || border_color.0.a() == 0.0 - || node.size().x <= 0. - || node.size().y <= 0. - { - continue; - } + continue; + } - // Both vertical and horizontal percentage border values are calculated based on the width of the parent node - // - let parent_width = parent - .and_then(|parent| parent_node_query.get(parent.get()).ok()) - .map(|parent_node| parent_node.size().x) - .unwrap_or(ui_logical_viewport_size.x); - let left = - resolve_border_thickness(style.border.left, parent_width, ui_logical_viewport_size); - let right = resolve_border_thickness( - style.border.right, - parent_width, - ui_logical_viewport_size, - ); - let top = - resolve_border_thickness(style.border.top, parent_width, ui_logical_viewport_size); - let bottom = resolve_border_thickness( - style.border.bottom, - parent_width, - ui_logical_viewport_size, - ); + // Both vertical and horizontal percentage border values are calculated based on the width of the parent node + // + let parent_width = parent + .and_then(|parent| parent_node_query.get(parent.get()).ok()) + .map(|parent_node| parent_node.size().x) + .unwrap_or(ui_logical_viewport_size.x); + let left = + resolve_border_thickness(style.border.left, parent_width, ui_logical_viewport_size); + let right = + resolve_border_thickness(style.border.right, parent_width, ui_logical_viewport_size); + let top = + resolve_border_thickness(style.border.top, parent_width, ui_logical_viewport_size); + let bottom = + resolve_border_thickness(style.border.bottom, parent_width, ui_logical_viewport_size); + + // Calculate the border rects, ensuring no overlap. + // The border occupies the space between the node's bounding rect and the node's bounding rect inset in each direction by the node's corresponding border value. + let max = 0.5 * node.size(); + let min = -max; + let inner_min = min + Vec2::new(left, top); + let inner_max = (max - Vec2::new(right, bottom)).max(inner_min); + let border_rects = [ + // Left border + Rect { + min, + max: Vec2::new(inner_min.x, max.y), + }, + // Right border + Rect { + min: Vec2::new(inner_max.x, min.y), + max, + }, + // Top border + Rect { + min: Vec2::new(inner_min.x, min.y), + max: Vec2::new(inner_max.x, inner_min.y), + }, + // Bottom border + Rect { + min: Vec2::new(inner_min.x, inner_max.y), + max: Vec2::new(inner_max.x, max.y), + }, + ]; - // Calculate the border rects, ensuring no overlap. - // The border occupies the space between the node's bounding rect and the node's bounding rect inset in each direction by the node's corresponding border value. - let max = 0.5 * node.size(); - let min = -max; - let inner_min = min + Vec2::new(left, top); - let inner_max = (max - Vec2::new(right, bottom)).max(inner_min); - let border_rects = [ - // Left border - Rect { - min, - max: Vec2::new(inner_min.x, max.y), - }, - // Right border - Rect { - min: Vec2::new(inner_max.x, min.y), - max, - }, - // Top border - Rect { - min: Vec2::new(inner_min.x, min.y), - max: Vec2::new(inner_max.x, inner_min.y), - }, - // Bottom border - Rect { - min: Vec2::new(inner_min.x, inner_max.y), - max: Vec2::new(inner_max.x, max.y), - }, - ]; - - let transform = global_transform.compute_matrix(); - - for edge in border_rects { - if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - extracted_uinodes.uinodes.push(ExtractedUiNode { - stack_index, - // This translates the uinode's transform to the center of the current border rectangle - transform: transform * Mat4::from_translation(edge.center().extend(0.)), - color: border_color.0, - rect: Rect { - max: edge.size(), - ..Default::default() - }, - image: image.clone_weak(), - atlas_size: None, - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); - } + let transform = global_transform.compute_matrix(); + + for edge in border_rects { + if edge.min.x < edge.max.x && edge.min.y < edge.max.y { + extracted_uinodes.uinodes.push(ExtractedUiNode { + stack_index: stack_index.0, + // This translates the uinode's transform to the center of the current border rectangle + transform: transform * Mat4::from_translation(edge.center().extend(0.)), + color: border_color.0, + rect: Rect { + max: edge.size(), + ..Default::default() + }, + image: image.clone_weak(), + atlas_size: None, + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + }); } } } @@ -384,6 +384,7 @@ pub fn extract_uinodes( uinode_query: Extract< Query< ( + &UiStackIndex, &Node, &GlobalTransform, &BackgroundColor, @@ -395,40 +396,38 @@ pub fn extract_uinodes( >, >, ) { - for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((uinode, transform, color, maybe_image, visibility, clip)) = - uinode_query.get(*entity) - { - // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { + for (stack_index, uinode, transform, color, maybe_image, visibility, clip) in + uinode_query.iter() + { + // Skip invisible and completely transparent nodes + if !visibility.is_visible() || color.0.a() == 0.0 { + continue; + } + + let (image, flip_x, flip_y) = if let Some(image) = maybe_image { + // Skip loading images + if !images.contains(&image.texture) { continue; } - - let (image, flip_x, flip_y) = if let Some(image) = maybe_image { - // Skip loading images - if !images.contains(&image.texture) { - continue; - } - (image.texture.clone_weak(), image.flip_x, image.flip_y) - } else { - (DEFAULT_IMAGE_HANDLE.typed(), false, false) - }; - - extracted_uinodes.uinodes.push(ExtractedUiNode { - stack_index, - transform: transform.compute_matrix(), - color: color.0, - rect: Rect { - min: Vec2::ZERO, - max: uinode.calculated_size, - }, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: None, - flip_x, - flip_y, - }); + (image.texture.clone_weak(), image.flip_x, image.flip_y) + } else { + (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; + + extracted_uinodes.uinodes.push(ExtractedUiNode { + stack_index: stack_index.0, + transform: transform.compute_matrix(), + color: color.0, + rect: Rect { + min: Vec2::ZERO, + max: uinode.calculated_size, + }, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: None, + flip_x, + flip_y, + }); } } @@ -514,6 +513,7 @@ pub fn extract_text_uinodes( ui_scale: Extract>, uinode_query: Extract< Query<( + &UiStackIndex, &Node, &GlobalTransform, &Text, @@ -532,48 +532,46 @@ pub fn extract_text_uinodes( let inverse_scale_factor = (scale_factor as f32).recip(); - for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() { - if let Ok((uinode, global_transform, text, text_layout_info, visibility, clip)) = - uinode_query.get(*entity) + for (stack_index, uinode, global_transform, text, text_layout_info, visibility, clip) in + uinode_query.iter() + { + // Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`) + if !visibility.is_visible() || uinode.size().x == 0. || uinode.size().y == 0. { + continue; + } + let transform = global_transform.compute_matrix() + * Mat4::from_translation(-0.5 * uinode.size().extend(0.)); + + let mut color = Color::WHITE; + let mut current_section = usize::MAX; + for PositionedGlyph { + position, + atlas_info, + section_index, + .. + } in &text_layout_info.glyphs { - // Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`) - if !visibility.is_visible() || uinode.size().x == 0. || uinode.size().y == 0. { - continue; + if *section_index != current_section { + color = text.sections[*section_index].style.color.as_rgba_linear(); + current_section = *section_index; } - let transform = global_transform.compute_matrix() - * Mat4::from_translation(-0.5 * uinode.size().extend(0.)); - - let mut color = Color::WHITE; - let mut current_section = usize::MAX; - for PositionedGlyph { - position, - atlas_info, - section_index, - .. - } in &text_layout_info.glyphs - { - if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); - current_section = *section_index; - } - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - let mut rect = atlas.textures[atlas_info.glyph_index]; - rect.min *= inverse_scale_factor; - rect.max *= inverse_scale_factor; - extracted_uinodes.uinodes.push(ExtractedUiNode { - stack_index, - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); - } + let mut rect = atlas.textures[atlas_info.glyph_index]; + rect.min *= inverse_scale_factor; + rect.max *= inverse_scale_factor; + extracted_uinodes.uinodes.push(ExtractedUiNode { + stack_index: stack_index.0, + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + }); } } } diff --git a/crates/bevy_ui/src/stack.rs b/crates/bevy_ui/src/stack.rs index c24a8aad66b26..84ee9b9fd52e2 100644 --- a/crates/bevy_ui/src/stack.rs +++ b/crates/bevy_ui/src/stack.rs @@ -3,7 +3,7 @@ use bevy_ecs::prelude::*; use bevy_hierarchy::prelude::*; -use crate::{Node, ZIndex}; +use crate::{Node, UiStackIndex, ZIndex}; /// The current UI stack, which contains all UI nodes ordered by their depth (back-to-front). /// @@ -35,6 +35,7 @@ pub fn ui_stack_system( root_node_query: Query, Without)>, zindex_query: Query<&ZIndex, With>, children_query: Query<&Children>, + mut stack_index_query: Query<&mut UiStackIndex>, ) { // Generate `StackingContext` tree let mut global_context = StackingContext::default(); @@ -55,6 +56,11 @@ pub fn ui_stack_system( ui_stack.uinodes.clear(); ui_stack.uinodes.reserve(total_entry_count); fill_stack_recursively(&mut ui_stack.uinodes, &mut global_context); + + for (i, entity) in ui_stack.uinodes.iter().enumerate() { + let mut stack_index = stack_index_query.get_mut(*entity).unwrap(); + stack_index.0 = i; + } } /// Generate z-index based UI node tree diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 7a41270eeb0e6..54d785359d924 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -78,6 +78,9 @@ impl Default for Node { } } +#[derive(Component, Copy, Clone, Debug, Default)] +pub struct UiStackIndex(pub usize); + /// Represents the possible value types for layout properties. /// /// This enum allows specifying values for various [`Style`] properties in different units, From c3a1716e29975e54035b0df0592931d470284dd3 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 11:50:07 +0100 Subject: [PATCH 17/60] Removed unused `UiStack` parameters --- crates/bevy_ui/src/render/mod.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 8f57475393c2e..3451d26366b22 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -12,7 +12,7 @@ pub use render_pass::*; use crate::UiStackIndex; use crate::{ prelude::UiCameraConfig, BackgroundColor, BorderColor, CalculatedClip, ContentSize, Node, - Style, UiImage, UiScale, UiStack, UiTextureAtlasImage, Val, + Style, UiImage, UiScale, UiTextureAtlasImage, Val, }; use bevy_app::prelude::*; @@ -283,7 +283,6 @@ pub fn extract_uinode_borders( mut extracted_uinodes: ResMut, windows: Extract>>, ui_scale: Extract>, - ui_stack: Extract>, uinode_query: Extract< Query< ( @@ -394,7 +393,6 @@ pub fn extract_uinode_borders( pub fn extract_uinodes( mut extracted_uinodes: ResMut, images: Extract>>, - ui_stack: Extract>, uinode_query: Extract< Query< ( @@ -523,7 +521,6 @@ pub fn extract_text_uinodes( mut extracted_uinodes: ResMut, texture_atlases: Extract>>, windows: Extract>>, - ui_stack: Extract>, ui_scale: Extract>, uinode_query: Extract< Query<( @@ -640,12 +637,7 @@ pub fn prepare_uinodes( mut extracted_uinodes: ResMut, ) { ui_meta.vertices.clear(); - - // sort by ui stack index, starting from the deepest node - // extracted_uinodes - // .uinodes - // .sort_by_key(|node| node.stack_index); - + extracted_uinodes .indices .sort_by_key(|extracted_index| extracted_index.stack_index); From e87333110f1cd0edeb7e65508198a3243aa0e966 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 12:59:24 +0100 Subject: [PATCH 18/60] cargo fmt --- crates/bevy_ui/src/render/mod.rs | 108 +++++++++++++++++-------------- 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 3451d26366b22..8238b7d366656 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -254,16 +254,19 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - extracted_uinodes.push(stack_index.0, ExtractedUiNode { - transform: transform.compute_matrix(), - color: color.0, - rect: atlas_rect, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: Some(atlas_size), - flip_x: atlas_image.flip_x, - flip_y: atlas_image.flip_y, - }); + extracted_uinodes.push( + stack_index.0, + ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: atlas_rect, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: Some(atlas_size), + flip_x: atlas_image.flip_x, + flip_y: atlas_image.flip_y, + }, + ); } } @@ -370,21 +373,23 @@ pub fn extract_uinode_borders( for edge in border_rects { if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - extracted_uinodes.push(stack_index.0, ExtractedUiNode { - - // This translates the uinode's transform to the center of the current border rectangle - transform: transform * Mat4::from_translation(edge.center().extend(0.)), - color: border_color.0, - rect: Rect { - max: edge.size(), - ..Default::default() + extracted_uinodes.push( + stack_index.0, + ExtractedUiNode { + // This translates the uinode's transform to the center of the current border rectangle + transform: transform * Mat4::from_translation(edge.center().extend(0.)), + color: border_color.0, + rect: Rect { + max: edge.size(), + ..Default::default() + }, + image: image.clone_weak(), + atlas_size: None, + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, }, - image: image.clone_weak(), - atlas_size: None, - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); + ); } } } @@ -426,20 +431,22 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.push(stack_index.0, ExtractedUiNode { - - transform: transform.compute_matrix(), - color: color.0, - rect: Rect { - min: Vec2::ZERO, - max: uinode.calculated_size, + extracted_uinodes.push( + stack_index.0, + ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: Rect { + min: Vec2::ZERO, + max: uinode.calculated_size, + }, + clip: clip.map(|clip| clip.clip), + image, + atlas_size: None, + flip_x, + flip_y, }, - clip: clip.map(|clip| clip.clip), - image, - atlas_size: None, - flip_x, - flip_y, - }); + ); } } @@ -571,17 +578,20 @@ pub fn extract_text_uinodes( let mut rect = atlas.textures[atlas_info.glyph_index]; rect.min *= inverse_scale_factor; rect.max *= inverse_scale_factor; - extracted_uinodes.push(stack_index.0, ExtractedUiNode { - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); + extracted_uinodes.push( + stack_index.0, + ExtractedUiNode { + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + }, + ); } } } @@ -637,7 +647,7 @@ pub fn prepare_uinodes( mut extracted_uinodes: ResMut, ) { ui_meta.vertices.clear(); - + extracted_uinodes .indices .sort_by_key(|extracted_index| extracted_index.stack_index); From 2694777f163879ceadb5343f0717e56ed4068e01 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 15:42:23 +0100 Subject: [PATCH 19/60] `bevy_ui::render` * Made `ExtractedIndex` and the fields of `ExtractedUiNodes` private. * Added a `clear` method to `ExtractedUiNodes`. --- crates/bevy_ui/src/render/mod.rs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 8238b7d366656..d8baab8e16b87 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -165,17 +165,17 @@ pub struct ExtractedUiNode { pub flip_y: bool, } -#[derive(Resource, Default)] -pub struct ExtractedUiNodes { - pub indices: Vec, - pub uinodes: Vec, -} - -pub struct ExtractedIndex { +struct ExtractedIndex { stack_index: u32, uinode_index: u32, } +#[derive(Resource, Default)] +pub struct ExtractedUiNodes { + indices: Vec, + uinodes: Vec, +} + impl ExtractedUiNodes { pub fn push(&mut self, stack_index: usize, item: ExtractedUiNode) { self.indices.push(ExtractedIndex { @@ -184,6 +184,11 @@ impl ExtractedUiNodes { }); self.uinodes.push(item); } + + fn clear(&mut self) { + self.indices.clear(); + self.uinodes.clear(); + } } pub fn extract_atlas_uinodes( @@ -662,7 +667,6 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - //for extracted_uinode in extracted_uinodes.uinodes.drain(..) { for index in &extracted_uinodes.indices { let extracted_uinode = &extracted_uinodes.uinodes[index.uinode_index as usize]; let mode = if is_textured(&extracted_uinode.image) { @@ -792,7 +796,7 @@ pub fn prepare_uinodes( last_z = extracted_uinode.transform.w_axis[2]; end += QUAD_INDICES.len() as u32; } - + // if start != end, there is one last batch to process if start != end { commands.spawn(UiBatch { @@ -804,8 +808,7 @@ pub fn prepare_uinodes( ui_meta.vertices.write_buffer(&render_device, &render_queue); - extracted_uinodes.uinodes.clear(); - extracted_uinodes.indices.clear(); + extracted_uinodes.clear(); } #[derive(Resource, Default)] From 80d010ac14233a859c7c65666809269699c7ff3c Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 17:51:22 +0100 Subject: [PATCH 20/60] `bevy_ui::render` `ExtractedIndex` holds a range of indices instead of a single index. The `ExtractedUiNodes::push_multiple` method is used to push multiple nodes and store them contiguously. --- crates/bevy_ui/src/render/mod.rs | 257 ++++++++++++++++--------------- 1 file changed, 135 insertions(+), 122 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index d8baab8e16b87..06f0c7036c9ba 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -167,7 +167,8 @@ pub struct ExtractedUiNode { struct ExtractedIndex { stack_index: u32, - uinode_index: u32, + start: u32, + end: u32, } #[derive(Resource, Default)] @@ -180,11 +181,22 @@ impl ExtractedUiNodes { pub fn push(&mut self, stack_index: usize, item: ExtractedUiNode) { self.indices.push(ExtractedIndex { stack_index: stack_index as u32, - uinode_index: self.uinodes.len() as u32, + start: self.uinodes.len() as u32, + end: (self.uinodes.len() + 1) as u32 }); self.uinodes.push(item); } + pub fn push_multiple(&mut self, stack_index: usize, items: impl Iterator) { + let start = self.uinodes.len() as u32; + self.uinodes.extend(items); + self.indices.push(ExtractedIndex { + stack_index: stack_index as u32, + start, + end: self.uinodes.len() as u32 + }); + } + fn clear(&mut self) { self.indices.clear(); self.uinodes.clear(); @@ -667,134 +679,135 @@ pub fn prepare_uinodes( image.id() != DEFAULT_IMAGE_HANDLE.id() } - for index in &extracted_uinodes.indices { - let extracted_uinode = &extracted_uinodes.uinodes[index.uinode_index as usize]; - let mode = if is_textured(&extracted_uinode.image) { - if current_batch_image.id() != extracted_uinode.image.id() { - if is_textured(¤t_batch_image) && start != end { - commands.spawn(UiBatch { - range: start..end, - image: current_batch_image, - z: last_z, - }); - start = end; + for extracted_index in &extracted_uinodes.indices { + for extracted_uinode in &extracted_uinodes.uinodes[extracted_index.start as usize .. extracted_index.end as usize] { + let mode = if is_textured(&extracted_uinode.image) { + if current_batch_image.id() != extracted_uinode.image.id() { + if is_textured(¤t_batch_image) && start != end { + commands.spawn(UiBatch { + range: start..end, + image: current_batch_image, + z: last_z, + }); + start = end; + } + current_batch_image = extracted_uinode.image.clone_weak(); } - current_batch_image = extracted_uinode.image.clone_weak(); - } - TEXTURED_QUAD - } else { - // Untextured `UiBatch`es are never spawned within the loop. - // If all the `extracted_uinodes` are untextured a single untextured UiBatch will be spawned after the loop terminates. - UNTEXTURED_QUAD - }; + TEXTURED_QUAD + } else { + // Untextured `UiBatch`es are never spawned within the loop. + // If all the `extracted_uinodes` are untextured a single untextured UiBatch will be spawned after the loop terminates. + UNTEXTURED_QUAD + }; - let mut uinode_rect = extracted_uinode.rect; - - let rect_size = uinode_rect.size().extend(1.0); - - // Specify the corners of the node - let positions = QUAD_VERTEX_POSITIONS - .map(|pos| (extracted_uinode.transform * (pos * rect_size).extend(1.)).xyz()); - - // Calculate the effect of clipping - // Note: this won't work with rotation/scaling, but that's much more complex (may need more that 2 quads) - let mut positions_diff = if let Some(clip) = extracted_uinode.clip { - [ - Vec2::new( - f32::max(clip.min.x - positions[0].x, 0.), - f32::max(clip.min.y - positions[0].y, 0.), - ), - Vec2::new( - f32::min(clip.max.x - positions[1].x, 0.), - f32::max(clip.min.y - positions[1].y, 0.), - ), - Vec2::new( - f32::min(clip.max.x - positions[2].x, 0.), - f32::min(clip.max.y - positions[2].y, 0.), - ), - Vec2::new( - f32::max(clip.min.x - positions[3].x, 0.), - f32::min(clip.max.y - positions[3].y, 0.), - ), - ] - } else { - [Vec2::ZERO; 4] - }; + let mut uinode_rect = extracted_uinode.rect; - let positions_clipped = [ - positions[0] + positions_diff[0].extend(0.), - positions[1] + positions_diff[1].extend(0.), - positions[2] + positions_diff[2].extend(0.), - positions[3] + positions_diff[3].extend(0.), - ]; + let rect_size = uinode_rect.size().extend(1.0); - let transformed_rect_size = extracted_uinode.transform.transform_vector3(rect_size); - - // Don't try to cull nodes that have a rotation - // In a rotation around the Z-axis, this value is 0.0 for an angle of 0.0 or Ï€ - // In those two cases, the culling check can proceed normally as corners will be on - // horizontal / vertical lines - // For all other angles, bypass the culling check - // This does not properly handles all rotations on all axis - if extracted_uinode.transform.x_axis[1] == 0.0 { - // Cull nodes that are completely clipped - if positions_diff[0].x - positions_diff[1].x >= transformed_rect_size.x - || positions_diff[1].y - positions_diff[2].y >= transformed_rect_size.y - { - continue; - } - } - let uvs = if mode == UNTEXTURED_QUAD { - [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y] - } else { - let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max); - if extracted_uinode.flip_x { - std::mem::swap(&mut uinode_rect.max.x, &mut uinode_rect.min.x); - positions_diff[0].x *= -1.; - positions_diff[1].x *= -1.; - positions_diff[2].x *= -1.; - positions_diff[3].x *= -1.; + // Specify the corners of the node + let positions = QUAD_VERTEX_POSITIONS + .map(|pos| (extracted_uinode.transform * (pos * rect_size).extend(1.)).xyz()); + + // Calculate the effect of clipping + // Note: this won't work with rotation/scaling, but that's much more complex (may need more that 2 quads) + let mut positions_diff = if let Some(clip) = extracted_uinode.clip { + [ + Vec2::new( + f32::max(clip.min.x - positions[0].x, 0.), + f32::max(clip.min.y - positions[0].y, 0.), + ), + Vec2::new( + f32::min(clip.max.x - positions[1].x, 0.), + f32::max(clip.min.y - positions[1].y, 0.), + ), + Vec2::new( + f32::min(clip.max.x - positions[2].x, 0.), + f32::min(clip.max.y - positions[2].y, 0.), + ), + Vec2::new( + f32::max(clip.min.x - positions[3].x, 0.), + f32::min(clip.max.y - positions[3].y, 0.), + ), + ] + } else { + [Vec2::ZERO; 4] + }; + + let positions_clipped = [ + positions[0] + positions_diff[0].extend(0.), + positions[1] + positions_diff[1].extend(0.), + positions[2] + positions_diff[2].extend(0.), + positions[3] + positions_diff[3].extend(0.), + ]; + + let transformed_rect_size = extracted_uinode.transform.transform_vector3(rect_size); + + // Don't try to cull nodes that have a rotation + // In a rotation around the Z-axis, this value is 0.0 for an angle of 0.0 or Ï€ + // In those two cases, the culling check can proceed normally as corners will be on + // horizontal / vertical lines + // For all other angles, bypass the culling check + // This does not properly handles all rotations on all axis + if extracted_uinode.transform.x_axis[1] == 0.0 { + // Cull nodes that are completely clipped + if positions_diff[0].x - positions_diff[1].x >= transformed_rect_size.x + || positions_diff[1].y - positions_diff[2].y >= transformed_rect_size.y + { + continue; + } } - if extracted_uinode.flip_y { - std::mem::swap(&mut uinode_rect.max.y, &mut uinode_rect.min.y); - positions_diff[0].y *= -1.; - positions_diff[1].y *= -1.; - positions_diff[2].y *= -1.; - positions_diff[3].y *= -1.; + let uvs = if mode == UNTEXTURED_QUAD { + [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y] + } else { + let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max); + if extracted_uinode.flip_x { + std::mem::swap(&mut uinode_rect.max.x, &mut uinode_rect.min.x); + positions_diff[0].x *= -1.; + positions_diff[1].x *= -1.; + positions_diff[2].x *= -1.; + positions_diff[3].x *= -1.; + } + if extracted_uinode.flip_y { + std::mem::swap(&mut uinode_rect.max.y, &mut uinode_rect.min.y); + positions_diff[0].y *= -1.; + positions_diff[1].y *= -1.; + positions_diff[2].y *= -1.; + positions_diff[3].y *= -1.; + } + [ + Vec2::new( + uinode_rect.min.x + positions_diff[0].x, + uinode_rect.min.y + positions_diff[0].y, + ), + Vec2::new( + uinode_rect.max.x + positions_diff[1].x, + uinode_rect.min.y + positions_diff[1].y, + ), + Vec2::new( + uinode_rect.max.x + positions_diff[2].x, + uinode_rect.max.y + positions_diff[2].y, + ), + Vec2::new( + uinode_rect.min.x + positions_diff[3].x, + uinode_rect.max.y + positions_diff[3].y, + ), + ] + .map(|pos| pos / atlas_extent) + }; + + let color = extracted_uinode.color.as_linear_rgba_f32(); + for i in QUAD_INDICES { + ui_meta.vertices.push(UiVertex { + position: positions_clipped[i].into(), + uv: uvs[i].into(), + color, + mode, + }); } - [ - Vec2::new( - uinode_rect.min.x + positions_diff[0].x, - uinode_rect.min.y + positions_diff[0].y, - ), - Vec2::new( - uinode_rect.max.x + positions_diff[1].x, - uinode_rect.min.y + positions_diff[1].y, - ), - Vec2::new( - uinode_rect.max.x + positions_diff[2].x, - uinode_rect.max.y + positions_diff[2].y, - ), - Vec2::new( - uinode_rect.min.x + positions_diff[3].x, - uinode_rect.max.y + positions_diff[3].y, - ), - ] - .map(|pos| pos / atlas_extent) - }; - let color = extracted_uinode.color.as_linear_rgba_f32(); - for i in QUAD_INDICES { - ui_meta.vertices.push(UiVertex { - position: positions_clipped[i].into(), - uv: uvs[i].into(), - color, - mode, - }); + last_z = extracted_uinode.transform.w_axis[2]; + end += QUAD_INDICES.len() as u32; } - - last_z = extracted_uinode.transform.w_axis[2]; - end += QUAD_INDICES.len() as u32; } // if start != end, there is one last batch to process From 17e0233d16c273f6005bc43569ca69045ddc8ca6 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 25 Jul 2023 18:19:51 +0100 Subject: [PATCH 21/60] use `push_multiple` for text and border ui extractions --- crates/bevy_ui/src/render/mod.rs | 102 +++++++++++++++---------------- 1 file changed, 50 insertions(+), 52 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 06f0c7036c9ba..839d2fdc33b39 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -387,28 +387,27 @@ pub fn extract_uinode_borders( ]; let transform = global_transform.compute_matrix(); - - for edge in border_rects { - if edge.min.x < edge.max.x && edge.min.y < edge.max.y { - extracted_uinodes.push( - stack_index.0, + extracted_uinodes.push_multiple(stack_index.0, + border_rects.into_iter() + .filter(|edge| edge.min.x < edge.max.x && edge.min.y < edge.max.y) + .map(|edge| { ExtractedUiNode { - // This translates the uinode's transform to the center of the current border rectangle - transform: transform * Mat4::from_translation(edge.center().extend(0.)), - color: border_color.0, - rect: Rect { - max: edge.size(), - ..Default::default() - }, - image: image.clone_weak(), - atlas_size: None, - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }, - ); - } - } + // This translates the uinode's transform to the center of the current border rectangle + transform: transform * Mat4::from_translation(edge.center().extend(0.)), + color: border_color.0, + rect: Rect { + max: edge.size(), + ..Default::default() + }, + image: image.clone_weak(), + atlas_size: None, + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + } + } + ) + ); } } @@ -579,37 +578,36 @@ pub fn extract_text_uinodes( let mut color = Color::WHITE; let mut current_section = usize::MAX; - for PositionedGlyph { - position, - atlas_info, - section_index, - .. - } in &text_layout_info.glyphs - { - if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); - current_section = *section_index; - } - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - - let mut rect = atlas.textures[atlas_info.glyph_index]; - rect.min *= inverse_scale_factor; - rect.max *= inverse_scale_factor; - extracted_uinodes.push( - stack_index.0, - ExtractedUiNode { - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }, - ); - } + extracted_uinodes.push_multiple(stack_index.0, + text_layout_info.glyphs + .iter() + .map(|PositionedGlyph { position, + atlas_info, + section_index, + ..}| { + if *section_index != current_section { + color = text.sections[*section_index].style.color.as_rgba_linear(); + current_section = *section_index; + } + let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + + let mut rect = atlas.textures[atlas_info.glyph_index]; + rect.min *= inverse_scale_factor; + rect.max *= inverse_scale_factor; + ExtractedUiNode { + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + } + } + )); + } } From 091450f1ee7711c68469738890a0cb86f026c6c4 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 26 Jul 2023 19:28:10 +0100 Subject: [PATCH 22/60] `bevy_ui::render` Renamed `ExtractedUiNodes::push` and `ExtractedUiNodes::push_multiple` to `ExtractedUiNodes::push_node` and `ExtractedUiNodes::push_nodes` respectively. --- crates/bevy_ui/src/render/mod.rs | 113 ++++++++++++++++--------------- 1 file changed, 59 insertions(+), 54 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 839d2fdc33b39..6d79f054a5044 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -178,22 +178,22 @@ pub struct ExtractedUiNodes { } impl ExtractedUiNodes { - pub fn push(&mut self, stack_index: usize, item: ExtractedUiNode) { + pub fn push_node(&mut self, stack_index: usize, item: ExtractedUiNode) { self.indices.push(ExtractedIndex { stack_index: stack_index as u32, start: self.uinodes.len() as u32, - end: (self.uinodes.len() + 1) as u32 + end: (self.uinodes.len() + 1) as u32, }); self.uinodes.push(item); } - pub fn push_multiple(&mut self, stack_index: usize, items: impl Iterator) { + pub fn push_nodes(&mut self, stack_index: usize, items: impl Iterator) { let start = self.uinodes.len() as u32; self.uinodes.extend(items); self.indices.push(ExtractedIndex { stack_index: stack_index as u32, start, - end: self.uinodes.len() as u32 + end: self.uinodes.len() as u32, }); } @@ -271,7 +271,7 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - extracted_uinodes.push( + extracted_uinodes.push_node( stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -387,26 +387,27 @@ pub fn extract_uinode_borders( ]; let transform = global_transform.compute_matrix(); - extracted_uinodes.push_multiple(stack_index.0, - border_rects.into_iter() - .filter(|edge| edge.min.x < edge.max.x && edge.min.y < edge.max.y) - .map(|edge| { + extracted_uinodes.push_nodes( + stack_index.0, + border_rects + .into_iter() + .filter(|edge| edge.min.x < edge.max.x && edge.min.y < edge.max.y) + .map(|edge| { ExtractedUiNode { - // This translates the uinode's transform to the center of the current border rectangle - transform: transform * Mat4::from_translation(edge.center().extend(0.)), - color: border_color.0, - rect: Rect { - max: edge.size(), - ..Default::default() - }, - image: image.clone_weak(), - atlas_size: None, - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - } + // This translates the uinode's transform to the center of the current border rectangle + transform: transform * Mat4::from_translation(edge.center().extend(0.)), + color: border_color.0, + rect: Rect { + max: edge.size(), + ..Default::default() + }, + image: image.clone_weak(), + atlas_size: None, + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, } - ) + }), ); } } @@ -447,7 +448,7 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.push( + extracted_uinodes.push_node( stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -578,36 +579,38 @@ pub fn extract_text_uinodes( let mut color = Color::WHITE; let mut current_section = usize::MAX; - extracted_uinodes.push_multiple(stack_index.0, - text_layout_info.glyphs - .iter() - .map(|PositionedGlyph { position, - atlas_info, - section_index, - ..}| { - if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); - current_section = *section_index; - } - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - - let mut rect = atlas.textures[atlas_info.glyph_index]; - rect.min *= inverse_scale_factor; - rect.max *= inverse_scale_factor; - ExtractedUiNode { - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - } + extracted_uinodes.push_nodes( + stack_index.0, + text_layout_info.glyphs.iter().map( + |PositionedGlyph { + position, + atlas_info, + section_index, + .. + }| { + if *section_index != current_section { + color = text.sections[*section_index].style.color.as_rgba_linear(); + current_section = *section_index; } - )); + let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + let mut rect = atlas.textures[atlas_info.glyph_index]; + rect.min *= inverse_scale_factor; + rect.max *= inverse_scale_factor; + ExtractedUiNode { + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + } + }, + ), + ); } } @@ -678,7 +681,9 @@ pub fn prepare_uinodes( } for extracted_index in &extracted_uinodes.indices { - for extracted_uinode in &extracted_uinodes.uinodes[extracted_index.start as usize .. extracted_index.end as usize] { + for extracted_uinode in + &extracted_uinodes.uinodes[extracted_index.start as usize..extracted_index.end as usize] + { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { @@ -807,7 +812,7 @@ pub fn prepare_uinodes( end += QUAD_INDICES.len() as u32; } } - + // if start != end, there is one last batch to process if start != end { commands.spawn(UiBatch { From e44278eb8ccdc8fa6cce3ce1bef1884bd0ce86df Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 27 Jul 2023 00:23:36 +0100 Subject: [PATCH 23/60] Added more doc comments. Renamed `ExtractedIndex` to `ExtractedSpan`. --- crates/bevy_ui/src/node_bundles.rs | 10 ++++++ crates/bevy_ui/src/render/mod.rs | 49 ++++++++++++++++-------------- crates/bevy_ui/src/stack.rs | 2 +- crates/bevy_ui/src/ui_node.rs | 2 +- 4 files changed, 38 insertions(+), 25 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index 308f90e57a7be..401e6bec6b23c 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -50,6 +50,8 @@ pub struct NodeBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -113,6 +115,8 @@ pub struct ImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -159,6 +163,8 @@ pub struct AtlasImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -197,6 +203,8 @@ pub struct TextBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, @@ -311,6 +319,8 @@ pub struct ButtonBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 6d79f054a5044..d0a557d4bda0f 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -165,40 +165,47 @@ pub struct ExtractedUiNode { pub flip_y: bool, } -struct ExtractedIndex { +struct ExtractedSpan { stack_index: u32, - start: u32, - end: u32, + range: Range, +} + +impl ExtractedSpan { + #[inline] + pub fn range(&self) -> Range { + self.range.start as usize .. self.range.end as usize + } } #[derive(Resource, Default)] pub struct ExtractedUiNodes { - indices: Vec, + spans: Vec, uinodes: Vec, } impl ExtractedUiNodes { - pub fn push_node(&mut self, stack_index: usize, item: ExtractedUiNode) { - self.indices.push(ExtractedIndex { - stack_index: stack_index as u32, - start: self.uinodes.len() as u32, - end: (self.uinodes.len() + 1) as u32, + /// Add a single `ExtractedUiNode` for rendering. + pub fn push_node(&mut self, stack_index: u32, item: ExtractedUiNode) { + self.spans.push(ExtractedSpan { + stack_index, + range: self.uinodes.len() as u32..(self.uinodes.len() + 1) as u32, }); self.uinodes.push(item); } - pub fn push_nodes(&mut self, stack_index: usize, items: impl Iterator) { + /// Add multiple `ExtractedUiNode`s for rendering. + pub fn push_nodes(&mut self, stack_index: u32, items: impl Iterator) { let start = self.uinodes.len() as u32; self.uinodes.extend(items); - self.indices.push(ExtractedIndex { - stack_index: stack_index as u32, - start, - end: self.uinodes.len() as u32, + self.spans.push(ExtractedSpan { + stack_index, + range: start..self.uinodes.len() as u32, }); } + /// Clear the buffers fn clear(&mut self) { - self.indices.clear(); + self.spans.clear(); self.uinodes.clear(); } } @@ -667,22 +674,18 @@ pub fn prepare_uinodes( ui_meta.vertices.clear(); extracted_uinodes - .indices + .spans .sort_by_key(|extracted_index| extracted_index.stack_index); let mut start = 0; let mut end = 0; let mut current_batch_image = DEFAULT_IMAGE_HANDLE.typed(); let mut last_z = 0.0; + let is_textured = |image: &Handle| image.id() != DEFAULT_IMAGE_HANDLE.id(); - #[inline] - fn is_textured(image: &Handle) -> bool { - image.id() != DEFAULT_IMAGE_HANDLE.id() - } - - for extracted_index in &extracted_uinodes.indices { + for extracted_span in &extracted_uinodes.spans { for extracted_uinode in - &extracted_uinodes.uinodes[extracted_index.start as usize..extracted_index.end as usize] + &extracted_uinodes.uinodes[extracted_span.range()] { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { diff --git a/crates/bevy_ui/src/stack.rs b/crates/bevy_ui/src/stack.rs index 84ee9b9fd52e2..71ee92c922155 100644 --- a/crates/bevy_ui/src/stack.rs +++ b/crates/bevy_ui/src/stack.rs @@ -59,7 +59,7 @@ pub fn ui_stack_system( for (i, entity) in ui_stack.uinodes.iter().enumerate() { let mut stack_index = stack_index_query.get_mut(*entity).unwrap(); - stack_index.0 = i; + stack_index.0 = i as u32; } } diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 54d785359d924..286b91bd0c0aa 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -79,7 +79,7 @@ impl Default for Node { } #[derive(Component, Copy, Clone, Debug, Default)] -pub struct UiStackIndex(pub usize); +pub struct UiStackIndex(pub u32); /// Represents the possible value types for layout properties. /// From fbeffc25cde79813bcf63d932fdf32615dfa9a56 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 27 Jul 2023 00:42:12 +0100 Subject: [PATCH 24/60] complete merge --- crates/bevy_ui/src/render/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 9fada07338cac..d0a557d4bda0f 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -42,7 +42,6 @@ use bevy_utils::FloatOrd; use bevy_utils::HashMap; use bytemuck::{Pod, Zeroable}; use std::ops::Range; -use std::vec::Drain; pub mod node { pub const UI_PASS_DRIVER: &str = "ui_pass_driver"; @@ -328,7 +327,6 @@ pub fn extract_uinode_borders( >, parent_node_query: Extract>>, ) { - let output_buffer = extracted_uinodes.next_buffer(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -567,7 +565,6 @@ pub fn extract_text_uinodes( )>, >, ) { - let output_buffer = extracted_uinodes.next_buffer(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() From a122eef640014083ef62b29bd55f7ef4dbb333e7 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 27 Jul 2023 00:50:16 +0100 Subject: [PATCH 25/60] cargo fmt --all --- crates/bevy_ui/src/node_bundles.rs | 10 +++++----- crates/bevy_ui/src/render/mod.rs | 6 ++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index 401e6bec6b23c..7a9c55caae4fc 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -50,7 +50,7 @@ pub struct NodeBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, - /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -115,7 +115,7 @@ pub struct ImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, - /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -163,7 +163,7 @@ pub struct AtlasImageBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, - /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } @@ -203,7 +203,7 @@ pub struct TextBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, - /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, /// The background color that will fill the containing node @@ -319,7 +319,7 @@ pub struct ButtonBundle { pub computed_visibility: ComputedVisibility, /// Indicates the depth at which the node should appear in the UI pub z_index: ZIndex, - /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. + /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. pub stack_index: UiStackIndex, } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index d0a557d4bda0f..64feb4245660a 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -173,7 +173,7 @@ struct ExtractedSpan { impl ExtractedSpan { #[inline] pub fn range(&self) -> Range { - self.range.start as usize .. self.range.end as usize + self.range.start as usize..self.range.end as usize } } @@ -684,9 +684,7 @@ pub fn prepare_uinodes( let is_textured = |image: &Handle| image.id() != DEFAULT_IMAGE_HANDLE.id(); for extracted_span in &extracted_uinodes.spans { - for extracted_uinode in - &extracted_uinodes.uinodes[extracted_span.range()] - { + for extracted_uinode in &extracted_uinodes.uinodes[extracted_span.range()] { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { if is_textured(¤t_batch_image) && start != end { From 923b53b497fca654e968b8176c987d9e77655b90 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 9 Aug 2023 15:46:50 +0100 Subject: [PATCH 26/60] `bevy_ui_node::UiStackIndex`: * Changed inner value to `pub(crate)` and added an accessor method. * Added derive `Reflect` and reflect `Component`, `Default` attributes * Added a `register_type` to UIPlugin. `bevy_ui_node::ExtractedRange`: * Renamed from `ExtractedSpan`, `spans` field of `ExtractedUiNode` also renamed to `ranges`. --- crates/bevy_ui/src/lib.rs | 1 + crates/bevy_ui/src/render/mod.rs | 16 ++++++++-------- crates/bevy_ui/src/ui_node.rs | 11 +++++++++-- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/bevy_ui/src/lib.rs b/crates/bevy_ui/src/lib.rs index d5669288c3db4..a1a39f802fe55 100644 --- a/crates/bevy_ui/src/lib.rs +++ b/crates/bevy_ui/src/lib.rs @@ -119,6 +119,7 @@ impl Plugin for UiPlugin { .register_type::() .register_type::() .register_type::() + .register_type::() .register_type::() .add_systems( PreUpdate, diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 64feb4245660a..d7782dee3d83c 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -165,12 +165,12 @@ pub struct ExtractedUiNode { pub flip_y: bool, } -struct ExtractedSpan { +struct ExtractedRange { stack_index: u32, range: Range, } -impl ExtractedSpan { +impl ExtractedRange { #[inline] pub fn range(&self) -> Range { self.range.start as usize..self.range.end as usize @@ -179,14 +179,14 @@ impl ExtractedSpan { #[derive(Resource, Default)] pub struct ExtractedUiNodes { - spans: Vec, + ranges: Vec, uinodes: Vec, } impl ExtractedUiNodes { /// Add a single `ExtractedUiNode` for rendering. pub fn push_node(&mut self, stack_index: u32, item: ExtractedUiNode) { - self.spans.push(ExtractedSpan { + self.ranges.push(ExtractedRange { stack_index, range: self.uinodes.len() as u32..(self.uinodes.len() + 1) as u32, }); @@ -197,7 +197,7 @@ impl ExtractedUiNodes { pub fn push_nodes(&mut self, stack_index: u32, items: impl Iterator) { let start = self.uinodes.len() as u32; self.uinodes.extend(items); - self.spans.push(ExtractedSpan { + self.ranges.push(ExtractedRange { stack_index, range: start..self.uinodes.len() as u32, }); @@ -205,7 +205,7 @@ impl ExtractedUiNodes { /// Clear the buffers fn clear(&mut self) { - self.spans.clear(); + self.ranges.clear(); self.uinodes.clear(); } } @@ -674,7 +674,7 @@ pub fn prepare_uinodes( ui_meta.vertices.clear(); extracted_uinodes - .spans + .ranges .sort_by_key(|extracted_index| extracted_index.stack_index); let mut start = 0; @@ -683,7 +683,7 @@ pub fn prepare_uinodes( let mut last_z = 0.0; let is_textured = |image: &Handle| image.id() != DEFAULT_IMAGE_HANDLE.id(); - for extracted_span in &extracted_uinodes.spans { + for extracted_span in &extracted_uinodes.ranges { for extracted_uinode in &extracted_uinodes.uinodes[extracted_span.range()] { let mode = if is_textured(&extracted_uinode.image) { if current_batch_image.id() != extracted_uinode.image.id() { diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index 286b91bd0c0aa..ae700cb4041c7 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -78,8 +78,15 @@ impl Default for Node { } } -#[derive(Component, Copy, Clone, Debug, Default)] -pub struct UiStackIndex(pub u32); +#[derive(Component, Copy, Clone, Debug, Default, Reflect)] +#[reflect(Component, Default)] +pub struct UiStackIndex(pub(crate) u32); + +impl UiStackIndex { + pub fn get(self) -> u32 { + self.0 + } +} /// Represents the possible value types for layout properties. /// From 30d4c0e97852794c29b20d2117f7219302672303 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 9 Aug 2023 15:57:09 +0100 Subject: [PATCH 27/60] Added doc comments for `UiStackIndex` --- crates/bevy_ui/src/node_bundles.rs | 2 ++ crates/bevy_ui/src/ui_node.rs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index 7a9c55caae4fc..fda6e2b7c589f 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -52,6 +52,8 @@ pub struct NodeBundle { pub z_index: ZIndex, /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. + /// + /// This field is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index ae700cb4041c7..82508ce27f19f 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -78,11 +78,16 @@ impl Default for Node { } } +/// The position of the Ui Node in the [`UiStack`](`crate::UiStack`). +/// UI nodes with a higher `UiStackIndex` are drawn in front of those with a lower `UiStackIndex`. +/// +/// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). #[derive(Component, Copy, Clone, Debug, Default, Reflect)] #[reflect(Component, Default)] pub struct UiStackIndex(pub(crate) u32); impl UiStackIndex { + // returns the stack index value pub fn get(self) -> u32 { self.0 } From a16b6270063c1976692b03d776f453d48537b097 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 9 Aug 2023 16:13:34 +0100 Subject: [PATCH 28/60] fix merge --- crates/bevy_ui/src/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index ebe943bcc15a1..1755f9a6af909 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -351,7 +351,7 @@ pub fn extract_uinode_borders( // Both vertical and horizontal percentage border values are calculated based on the width of the parent node // let parent_width = parent - .and_then(|parent| parent_node_query.get(parent.get()).ok()) + .and_then(|parent| node_query.get(parent.get()).ok()) .map(|parent_node| parent_node.size().x) .unwrap_or(ui_logical_viewport_size.x); let left = From 1f2a3339a576e8fee79e8e13d7c7f74f204570bc Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 9 Aug 2023 18:50:12 +0100 Subject: [PATCH 29/60] doc comments fix --- crates/bevy_ui/src/node_bundles.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index fda6e2b7c589f..bed53869ccffa 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -53,7 +53,7 @@ pub struct NodeBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This field is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -119,6 +119,8 @@ pub struct ImageBundle { pub z_index: ZIndex, /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. + /// + /// This field is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -167,6 +169,8 @@ pub struct AtlasImageBundle { pub z_index: ZIndex, /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. + /// + /// This field is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -207,8 +211,10 @@ pub struct TextBundle { pub z_index: ZIndex, /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. + /// + /// This field is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, - /// The background color that will fill the containing node + /// The background color that will fill the containing node pub background_color: BackgroundColor, } @@ -323,6 +329,8 @@ pub struct ButtonBundle { pub z_index: ZIndex, /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. + /// + /// This field is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } From 4def5562fd68a458afd991ea8aa2597b3d3c207e Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 9 Aug 2023 20:13:58 +0100 Subject: [PATCH 30/60] Fixed doc comments for UI node bundles, replacing `field` with `component`. The bundle's field isn't the thing being managed by the system. --- crates/bevy_ui/src/node_bundles.rs | 38 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index bed53869ccffa..ec569e94fa227 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -36,12 +36,12 @@ pub struct NodeBundle { pub focus_policy: FocusPolicy, /// The transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub transform: Transform, /// The global transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub global_transform: GlobalTransform, /// Describes the visibility properties of the node @@ -53,7 +53,7 @@ pub struct NodeBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by `ui_stack_system`. + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -81,7 +81,7 @@ impl Default for NodeBundle { pub struct ImageBundle { /// Describes the logical size of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub node: Node, /// Styles which control the layout (size and position) of the node and it's children @@ -97,18 +97,18 @@ pub struct ImageBundle { pub image: UiImage, /// The size of the image in pixels /// - /// This field is set automatically + /// This component is set automatically pub image_size: UiImageSize, /// Whether this node should block interaction with lower nodes pub focus_policy: FocusPolicy, /// The transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub transform: Transform, /// The global transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub global_transform: GlobalTransform, /// Describes the visibility properties of the node @@ -120,7 +120,7 @@ pub struct ImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by `ui_stack_system`. + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -129,7 +129,7 @@ pub struct ImageBundle { pub struct AtlasImageBundle { /// Describes the logical size of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub node: Node, /// Styles which control the layout (size and position) of the node and it's children @@ -149,16 +149,16 @@ pub struct AtlasImageBundle { pub focus_policy: FocusPolicy, /// The size of the image in pixels /// - /// This field is set automatically + /// This component is set automatically pub image_size: UiImageSize, /// The transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub transform: Transform, /// The global transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub global_transform: GlobalTransform, /// Describes the visibility properties of the node @@ -170,7 +170,7 @@ pub struct AtlasImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by `ui_stack_system`. + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -195,12 +195,12 @@ pub struct TextBundle { pub focus_policy: FocusPolicy, /// The transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub transform: Transform, /// The global transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub global_transform: GlobalTransform, /// Describes the visibility properties of the node @@ -212,7 +212,7 @@ pub struct TextBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by `ui_stack_system`. + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, @@ -313,12 +313,12 @@ pub struct ButtonBundle { pub image: UiImage, /// The transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub transform: Transform, /// The global transform of the node /// - /// This field is automatically managed by the UI layout system. + /// This component is automatically managed by the UI layout system. /// To alter the position of the `NodeBundle`, use the properties of the [`Style`] component. pub global_transform: GlobalTransform, /// Describes the visibility properties of the node @@ -330,7 +330,7 @@ pub struct ButtonBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This field is automatically managed by `ui_stack_system`. + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } From f223fc76f25281bf33345479083386122a4095b5 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 21 Aug 2023 00:14:56 +0100 Subject: [PATCH 31/60] Added links to node bundle comments --- crates/bevy_ui/src/node_bundles.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index ec569e94fa227..e3cdc28bc035f 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -53,7 +53,7 @@ pub struct NodeBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by `ui_stack_system`. + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -120,7 +120,7 @@ pub struct ImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by `ui_stack_system`. + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -170,7 +170,7 @@ pub struct AtlasImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by `ui_stack_system`. + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -212,7 +212,7 @@ pub struct TextBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by `ui_stack_system`. + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, @@ -330,7 +330,7 @@ pub struct ButtonBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by `ui_stack_system`. + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } From 3e106e2c255483ddebc8aaede32e56db4bcd829b Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 21 Aug 2023 00:15:25 +0100 Subject: [PATCH 32/60] Removed double space in comments --- crates/bevy_ui/src/node_bundles.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index e3cdc28bc035f..18b8784b3ecfa 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -53,7 +53,7 @@ pub struct NodeBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -120,7 +120,7 @@ pub struct ImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -170,7 +170,7 @@ pub struct AtlasImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } @@ -212,7 +212,7 @@ pub struct TextBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, @@ -330,7 +330,7 @@ pub struct ButtonBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). pub stack_index: UiStackIndex, } From aefe2056808da72b57995a71df57ff0dd2c004c9 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 21 Aug 2023 00:33:19 +0100 Subject: [PATCH 33/60] Removed doc comment links to private items. --- crates/bevy_ui/src/node_bundles.rs | 10 +++++----- crates/bevy_ui/src/ui_node.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/bevy_ui/src/node_bundles.rs b/crates/bevy_ui/src/node_bundles.rs index 18b8784b3ecfa..ec569e94fa227 100644 --- a/crates/bevy_ui/src/node_bundles.rs +++ b/crates/bevy_ui/src/node_bundles.rs @@ -53,7 +53,7 @@ pub struct NodeBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -120,7 +120,7 @@ pub struct ImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -170,7 +170,7 @@ pub struct AtlasImageBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } @@ -212,7 +212,7 @@ pub struct TextBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, /// The background color that will fill the containing node pub background_color: BackgroundColor, @@ -330,7 +330,7 @@ pub struct ButtonBundle { /// The node's position in the UiStack. Nodes with lower stack indices are drawn first. /// Nodes with a higher stack index are drawn over nodes with a lower stack index. /// - /// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). + /// This component is automatically managed by `ui_stack_system`. pub stack_index: UiStackIndex, } diff --git a/crates/bevy_ui/src/ui_node.rs b/crates/bevy_ui/src/ui_node.rs index cc197205d6dd1..8d907e47a9d26 100644 --- a/crates/bevy_ui/src/ui_node.rs +++ b/crates/bevy_ui/src/ui_node.rs @@ -81,7 +81,7 @@ impl Default for Node { /// The position of the Ui Node in the [`UiStack`](`crate::UiStack`). /// UI nodes with a higher `UiStackIndex` are drawn in front of those with a lower `UiStackIndex`. /// -/// This component is automatically managed by [`ui_stack_system`](`crate::stack::ui_stack_system`). +/// This component is automatically managed by `ui_stack_system`. #[derive(Component, Copy, Clone, Debug, Default, Reflect)] #[reflect(Component, Default)] pub struct UiStackIndex(pub(crate) u32); From a0eb2bd41414c5ddc1ab5aa88a3878fccd0f2e13 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 21 Aug 2023 16:38:48 +0100 Subject: [PATCH 34/60] fixed `text_ui_stack_system` --- crates/bevy_ui/src/stack.rs | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/crates/bevy_ui/src/stack.rs b/crates/bevy_ui/src/stack.rs index 71ee92c922155..077f7fcf1d767 100644 --- a/crates/bevy_ui/src/stack.rs +++ b/crates/bevy_ui/src/stack.rs @@ -126,23 +126,23 @@ mod tests { component::Component, schedule::Schedule, system::{CommandQueue, Commands}, - world::World, + world::World, entity::Entity, }; use bevy_hierarchy::BuildChildren; - use crate::{Node, UiStack, ZIndex}; + use crate::{Node, UiStack, ZIndex, UiStackIndex}; use super::ui_stack_system; #[derive(Component, PartialEq, Debug, Clone)] struct Label(&'static str); - fn node_with_zindex(name: &'static str, z_index: ZIndex) -> (Label, Node, ZIndex) { - (Label(name), Node::default(), z_index) + fn node_with_zindex(name: &'static str, z_index: ZIndex) -> (Label, Node, UiStackIndex, ZIndex) { + (Label(name), Node::default(), UiStackIndex::default(), z_index) } - fn node_without_zindex(name: &'static str) -> (Label, Node) { - (Label(name), Node::default()) + fn node_without_zindex(name: &'static str) -> (Label, Node, UiStackIndex) { + (Label(name), Node::default(), UiStackIndex::default()) } /// Tests the UI Stack system. @@ -205,14 +205,16 @@ mod tests { schedule.add_systems(ui_stack_system); schedule.run(&mut world); - let mut query = world.query::<&Label>(); + let mut query = world.query::<(&Label, &UiStackIndex)>(); let ui_stack = world.resource::(); let actual_result = ui_stack .uinodes .iter() - .map(|entity| query.get(&world, *entity).unwrap().clone()) - .collect::>(); - let expected_result = vec![ + .map(|entity| { + let (label, ui_stack_index) = query.get(&world, *entity).unwrap(); + (label.clone(), ui_stack_index.0) + }).collect::>(); + let expected_result: Vec<_> = [ (Label("1-2-1")), // ZIndex::Global(-3) (Label("3")), // ZIndex::Global(-2) (Label("1-2")), // ZIndex::Global(-1) @@ -231,7 +233,7 @@ mod tests { (Label("1-1")), (Label("1-3")), (Label("0")), // ZIndex::Global(2) - ]; - assert_eq!(actual_result, expected_result); + ].into_iter().enumerate().map(|(i, label)| { (label, i as u32) }).collect(); + assert_eq!(actual_result, expected_result); } } From 7194fc060b0a3c5e876a1a765ffe64d11832635f Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 21 Aug 2023 17:05:57 +0100 Subject: [PATCH 35/60] cargo fmt --- crates/bevy_ui/src/stack.rs | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ui/src/stack.rs b/crates/bevy_ui/src/stack.rs index 077f7fcf1d767..77fc3880367ac 100644 --- a/crates/bevy_ui/src/stack.rs +++ b/crates/bevy_ui/src/stack.rs @@ -124,21 +124,30 @@ fn fill_stack_recursively(result: &mut Vec, stack: &mut StackingContext) mod tests { use bevy_ecs::{ component::Component, + entity::Entity, schedule::Schedule, system::{CommandQueue, Commands}, - world::World, entity::Entity, + world::World, }; use bevy_hierarchy::BuildChildren; - use crate::{Node, UiStack, ZIndex, UiStackIndex}; + use crate::{Node, UiStack, UiStackIndex, ZIndex}; use super::ui_stack_system; #[derive(Component, PartialEq, Debug, Clone)] struct Label(&'static str); - fn node_with_zindex(name: &'static str, z_index: ZIndex) -> (Label, Node, UiStackIndex, ZIndex) { - (Label(name), Node::default(), UiStackIndex::default(), z_index) + fn node_with_zindex( + name: &'static str, + z_index: ZIndex, + ) -> (Label, Node, UiStackIndex, ZIndex) { + ( + Label(name), + Node::default(), + UiStackIndex::default(), + z_index, + ) } fn node_without_zindex(name: &'static str) -> (Label, Node, UiStackIndex) { @@ -213,7 +222,8 @@ mod tests { .map(|entity| { let (label, ui_stack_index) = query.get(&world, *entity).unwrap(); (label.clone(), ui_stack_index.0) - }).collect::>(); + }) + .collect::>(); let expected_result: Vec<_> = [ (Label("1-2-1")), // ZIndex::Global(-3) (Label("3")), // ZIndex::Global(-2) @@ -233,7 +243,11 @@ mod tests { (Label("1-1")), (Label("1-3")), (Label("0")), // ZIndex::Global(2) - ].into_iter().enumerate().map(|(i, label)| { (label, i as u32) }).collect(); - assert_eq!(actual_result, expected_result); + ] + .into_iter() + .enumerate() + .map(|(i, label)| (label, i as u32)) + .collect(); + assert_eq!(actual_result, expected_result); } } From 64bc7c69fc9a3b8857136180fe6133ee7c5fb351 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 23 Aug 2023 00:13:19 +0100 Subject: [PATCH 36/60] Removed unused import --- crates/bevy_ui/src/stack.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/src/stack.rs b/crates/bevy_ui/src/stack.rs index 77fc3880367ac..293bf2ca79c5e 100644 --- a/crates/bevy_ui/src/stack.rs +++ b/crates/bevy_ui/src/stack.rs @@ -124,7 +124,6 @@ fn fill_stack_recursively(result: &mut Vec, stack: &mut StackingContext) mod tests { use bevy_ecs::{ component::Component, - entity::Entity, schedule::Schedule, system::{CommandQueue, Commands}, world::World, From 1f476cbac5688b78ae9b40ede8fc88b72711f414 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 28 Aug 2023 16:07:47 +0100 Subject: [PATCH 37/60] Fixed UI batching so new batches aren't created for untextured nodes. Thee initial value of `batch_image_handle` is changed from `HandleId::Id(Uuid::nil(), u64::MAX)` to `DEFAULT_IMAGE_HANDLE.id()`, which allowed me to make the if-block simpler I think. The default image from `DEFAULT_IMAGE_HANDLE` is always inserted into `UiImageBindGroups` even if it's not used. I tried to add a check and only add it if there is only one batch but this crashed. --- crates/bevy_ui/src/render/mod.rs | 130 +++++++++++++++++-------------- 1 file changed, 73 insertions(+), 57 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 1305093c05047..67742fbab4891 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -38,8 +38,9 @@ use bevy_sprite::TextureAtlas; use bevy_text::{PositionedGlyph, Text, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; use bevy_utils::HashMap; -use bevy_utils::{FloatOrd, Uuid}; +use bevy_utils::FloatOrd; use bytemuck::{Pod, Zeroable}; +use std::mem::replace; use std::ops::Range; pub mod node { @@ -663,6 +664,7 @@ pub fn queue_uinodes( transparent_phase .items .reserve(extracted_uinodes.uinodes.len()); + for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() { transparent_phase.add(TransparentUi { draw_function, @@ -706,11 +708,6 @@ pub fn prepare_uinodes( }; } - #[inline] - fn is_textured(image: &Handle) -> bool { - image.id() != DEFAULT_IMAGE_HANDLE.id() - } - if let Some(view_binding) = view_uniforms.uniforms.binding() { let mut batches: Vec<(Entity, UiBatch)> = Vec::with_capacity(*previous_len); @@ -726,64 +723,85 @@ pub fn prepare_uinodes( // Vertex buffer index let mut index = 0; - for mut ui_phase in &mut phases { let mut batch_item_index = 0; - let mut batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); + let mut batch_image_handle = DEFAULT_IMAGE_HANDLE.id(); + + if let Some(gpu_image) = gpu_images.get(&DEFAULT_IMAGE_HANDLE.typed()) { + image_bind_groups + .values + .entry(Handle::weak(DEFAULT_IMAGE_HANDLE.id())) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView(&gpu_image.texture_view), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("ui_material_bind_group"), + layout: &ui_pipeline.image_layout, + }) + }); + } for item_index in 0..ui_phase.items.len() { let item = &mut ui_phase.items[item_index]; if let Some(extracted_uinode) = extracted_uinodes.uinodes.get(item.entity) { - let mut existing_batch = batches - .last_mut() - .filter(|_| batch_image_handle == extracted_uinode.image.id()); - - if existing_batch.is_none() { - if let Some(gpu_image) = gpu_images.get(&extracted_uinode.image) { - batch_item_index = item_index; - batch_image_handle = extracted_uinode.image.id(); - - let new_batch = UiBatch { - range: index..index, - image_handle_id: extracted_uinode.image.id(), - }; - - batches.push((item.entity, new_batch)); - - image_bind_groups - .values - .entry(Handle::weak(batch_image_handle)) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView( - &gpu_image.texture_view, - ), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler( - &gpu_image.sampler, - ), - }, - ], - label: Some("ui_material_bind_group"), - layout: &ui_pipeline.image_layout, - }) - }); - - existing_batch = batches.last_mut(); + let existing_batch = if extracted_uinode.image.id() == DEFAULT_IMAGE_HANDLE.id() + || extracted_uinode.image.id() == batch_image_handle + { + batches.last_mut() + } else if let Some(gpu_image) = gpu_images.get(&extracted_uinode.image) { + image_bind_groups + .values + .entry(Handle::weak(extracted_uinode.image.id())) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView( + &gpu_image.texture_view, + ), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("ui_material_bind_group"), + layout: &ui_pipeline.image_layout, + }) + }); + if replace(&mut batch_image_handle, extracted_uinode.image.id()) == DEFAULT_IMAGE_HANDLE.id() { + let existing_batch = batches.last_mut().unwrap(); + existing_batch.1.image_handle_id = extracted_uinode.image.id(); + Some(existing_batch) } else { - continue; + None } + } else { + continue; + }; + if existing_batch.is_none() { + batch_item_index = item_index; + let new_batch = UiBatch { + range: index..index, + image_handle_id: extracted_uinode.image.id(), + }; + + batches.push((item.entity, new_batch)); } - let mode = if is_textured(&extracted_uinode.image) { - TEXTURED_QUAD - } else { + let mode = if extracted_uinode.image.id() == DEFAULT_IMAGE_HANDLE.id() { UNTEXTURED_QUAD + } else { + TEXTURED_QUAD }; let mut uinode_rect = extracted_uinode.rect; @@ -893,11 +911,9 @@ pub fn prepare_uinodes( }); } index += QUAD_INDICES.len() as u32; - existing_batch.unwrap().1.range.end = index; + batches.last_mut().unwrap().1.range.end = index; ui_phase.items[batch_item_index].batch_size += 1; - } else { - batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); - } + } } } ui_meta.vertices.write_buffer(&render_device, &render_queue); From 6891a81f6bae4b445fea55c1da90d598cb5fd5b2 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 28 Aug 2023 16:27:01 +0100 Subject: [PATCH 38/60] cargo fmt --all --- crates/bevy_ui/src/render/mod.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 67742fbab4891..151d360d0e4a5 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -37,8 +37,8 @@ use bevy_sprite::TextureAtlas; #[cfg(feature = "bevy_text")] use bevy_text::{PositionedGlyph, Text, TextLayoutInfo}; use bevy_transform::components::GlobalTransform; -use bevy_utils::HashMap; use bevy_utils::FloatOrd; +use bevy_utils::HashMap; use bytemuck::{Pod, Zeroable}; use std::mem::replace; use std::ops::Range; @@ -778,7 +778,9 @@ pub fn prepare_uinodes( layout: &ui_pipeline.image_layout, }) }); - if replace(&mut batch_image_handle, extracted_uinode.image.id()) == DEFAULT_IMAGE_HANDLE.id() { + if replace(&mut batch_image_handle, extracted_uinode.image.id()) + == DEFAULT_IMAGE_HANDLE.id() + { let existing_batch = batches.last_mut().unwrap(); existing_batch.1.image_handle_id = extracted_uinode.image.id(); Some(existing_batch) @@ -913,7 +915,7 @@ pub fn prepare_uinodes( index += QUAD_INDICES.len() as u32; batches.last_mut().unwrap().1.range.end = index; ui_phase.items[batch_item_index].batch_size += 1; - } + } } } ui_meta.vertices.write_buffer(&render_device, &render_queue); From 4196697c940757b0f6d4f6ddb4187bff6bac1629 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 28 Aug 2023 16:54:15 +0100 Subject: [PATCH 39/60] Fixed panic, can't just unwrap the last element from batches to change the texture handle as we might not have created any batches yet. --- crates/bevy_ui/src/render/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 151d360d0e4a5..ac8aae5ac4413 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -781,9 +781,10 @@ pub fn prepare_uinodes( if replace(&mut batch_image_handle, extracted_uinode.image.id()) == DEFAULT_IMAGE_HANDLE.id() { - let existing_batch = batches.last_mut().unwrap(); - existing_batch.1.image_handle_id = extracted_uinode.image.id(); - Some(existing_batch) + batches.last_mut().map(|existing_batch| { + existing_batch.1.image_handle_id = batch_image_handle; + existing_batch + }) } else { None } From c6a9cd6b5d469a1d7d2789b5282339aa7539d334 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Mon, 28 Aug 2023 23:29:32 +0100 Subject: [PATCH 40/60] removed unused imports --- crates/bevy_ui/src/render/render_pass.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index 697aa11104c7e..d4b85ccdf7c65 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -12,7 +12,6 @@ use bevy_render::{ renderer::*, view::*, }; -use bevy_utils::FloatOrd; pub struct UiPassNode { ui_view_query: QueryState< @@ -87,7 +86,7 @@ impl Node for UiPassNode { } pub struct TransparentUi { - pub sort_key: FloatOrd, + pub sort_key: u32, pub entity: Entity, pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, @@ -95,7 +94,7 @@ pub struct TransparentUi { } impl PhaseItem for TransparentUi { - type SortKey = FloatOrd; + type SortKey = u32; #[inline] fn entity(&self) -> Entity { From 4e6661073e32648bab24898e2c2492070177be55 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 14:07:36 +0100 Subject: [PATCH 41/60] Reworked extraction to avoid the sparseset --- crates/bevy_ui/src/render/mod.rs | 201 +++-------------------- crates/bevy_ui/src/render/render_pass.rs | 8 +- 2 files changed, 22 insertions(+), 187 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index a11c843414acf..4a593a4fe0b00 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -180,15 +180,14 @@ impl ExtractedRange { #[derive(Resource, Default)] pub struct ExtractedUiNodes { //ranges: Vec, - pub ranges: SparseSet, + pub ranges: Vec, uinodes: Vec, } impl ExtractedUiNodes { /// Add a single `ExtractedUiNode` for rendering. - pub fn push_node(&mut self, entity: Entity, stack_index: u32, item: ExtractedUiNode) { - self.ranges.insert( - entity, + pub fn push_node(&mut self, stack_index: u32, item: ExtractedUiNode) { + self.ranges.push( ExtractedRange { stack_index, range: self.uinodes.len() as u32..(self.uinodes.len() + 1) as u32, @@ -197,11 +196,10 @@ impl ExtractedUiNodes { } /// Add multiple `ExtractedUiNode`s for rendering. - pub fn push_nodes(&mut self, entity: Entity, stack_index: u32, items: impl Iterator) { + pub fn push_nodes(&mut self, stack_index: u32, items: impl Iterator) { let start = self.uinodes.len() as u32; self.uinodes.extend(items); - self.ranges.insert( - entity, + self.ranges.push( ExtractedRange { stack_index, range: start..self.uinodes.len() as u32, @@ -285,7 +283,6 @@ pub fn extract_atlas_uinodes( atlas_size *= scale; extracted_uinodes.push_node( - commands.spawn_empty().id(), stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -403,7 +400,6 @@ pub fn extract_uinode_borders( let transform = global_transform.compute_matrix(); extracted_uinodes.push_nodes( - commands.spawn_empty().id(), stack_index.0, border_rects .into_iter() @@ -466,7 +462,6 @@ pub fn extract_uinodes( }; extracted_uinodes.push_node( - entity, stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -599,7 +594,6 @@ pub fn extract_text_uinodes( let mut color = Color::WHITE; let mut current_section = usize::MAX; extracted_uinodes.push_nodes( - commands.spawn_empty().id(), stack_index.0, text_layout_info.glyphs.iter().map( |PositionedGlyph { @@ -678,186 +672,32 @@ const UNTEXTURED_QUAD: u32 = 1; #[allow(clippy::too_many_arguments)] pub fn queue_uinodes( - extracted_uinodes: Res, + mut extracted_uinodes: ResMut, ui_pipeline: Res, mut pipelines: ResMut>, mut views: Query<(&ExtractedView, &mut RenderPhase)>, pipeline_cache: Res, draw_functions: Res>, + mut commands: Commands, ) { -// <<<<<<< HEAD -// ui_meta.vertices.clear(); - -// extracted_uinodes -// .ranges -// .sort_by_key(|extracted_index| extracted_index.stack_index); - -// let mut start = 0; -// let mut end = 0; -// let mut current_batch_image = DEFAULT_IMAGE_HANDLE.typed(); -// let mut last_z = 0.0; -// let is_textured = |image: &Handle| image.id() != DEFAULT_IMAGE_HANDLE.id(); - -// for extracted_span in &extracted_uinodes.ranges { -// for extracted_uinode in &extracted_uinodes.uinodes[extracted_span.range()] { -// let mode = if is_textured(&extracted_uinode.image) { -// if current_batch_image.id() != extracted_uinode.image.id() { -// if is_textured(¤t_batch_image) && start != end { -// commands.spawn(UiBatch { -// range: start..end, -// image: current_batch_image, -// z: last_z, -// }); -// start = end; -// } -// current_batch_image = extracted_uinode.image.clone_weak(); -// } -// TEXTURED_QUAD -// } else { -// // Untextured `UiBatch`es are never spawned within the loop. -// // If all the `extracted_uinodes` are untextured a single untextured UiBatch will be spawned after the loop terminates. -// UNTEXTURED_QUAD -// }; - -// let mut uinode_rect = extracted_uinode.rect; - -// let rect_size = uinode_rect.size().extend(1.0); - -// // Specify the corners of the node -// let positions = QUAD_VERTEX_POSITIONS -// .map(|pos| (extracted_uinode.transform * (pos * rect_size).extend(1.)).xyz()); - -// // Calculate the effect of clipping -// // Note: this won't work with rotation/scaling, but that's much more complex (may need more that 2 quads) -// let mut positions_diff = if let Some(clip) = extracted_uinode.clip { -// [ -// Vec2::new( -// f32::max(clip.min.x - positions[0].x, 0.), -// f32::max(clip.min.y - positions[0].y, 0.), -// ), -// Vec2::new( -// f32::min(clip.max.x - positions[1].x, 0.), -// f32::max(clip.min.y - positions[1].y, 0.), -// ), -// Vec2::new( -// f32::min(clip.max.x - positions[2].x, 0.), -// f32::min(clip.max.y - positions[2].y, 0.), -// ), -// Vec2::new( -// f32::max(clip.min.x - positions[3].x, 0.), -// f32::min(clip.max.y - positions[3].y, 0.), -// ), -// ] -// } else { -// [Vec2::ZERO; 4] -// }; - -// let positions_clipped = [ -// positions[0] + positions_diff[0].extend(0.), -// positions[1] + positions_diff[1].extend(0.), -// positions[2] + positions_diff[2].extend(0.), -// positions[3] + positions_diff[3].extend(0.), -// ]; - -// let transformed_rect_size = extracted_uinode.transform.transform_vector3(rect_size); - -// // Don't try to cull nodes that have a rotation -// // In a rotation around the Z-axis, this value is 0.0 for an angle of 0.0 or Ï€ -// // In those two cases, the culling check can proceed normally as corners will be on -// // horizontal / vertical lines -// // For all other angles, bypass the culling check -// // This does not properly handles all rotations on all axis -// if extracted_uinode.transform.x_axis[1] == 0.0 { -// // Cull nodes that are completely clipped -// if positions_diff[0].x - positions_diff[1].x >= transformed_rect_size.x -// || positions_diff[1].y - positions_diff[2].y >= transformed_rect_size.y -// { -// continue; -// } -// } -// let uvs = if mode == UNTEXTURED_QUAD { -// [Vec2::ZERO, Vec2::X, Vec2::ONE, Vec2::Y] -// } else { -// let atlas_extent = extracted_uinode.atlas_size.unwrap_or(uinode_rect.max); -// if extracted_uinode.flip_x { -// std::mem::swap(&mut uinode_rect.max.x, &mut uinode_rect.min.x); -// positions_diff[0].x *= -1.; -// positions_diff[1].x *= -1.; -// positions_diff[2].x *= -1.; -// positions_diff[3].x *= -1.; -// } -// if extracted_uinode.flip_y { -// std::mem::swap(&mut uinode_rect.max.y, &mut uinode_rect.min.y); -// positions_diff[0].y *= -1.; -// positions_diff[1].y *= -1.; -// positions_diff[2].y *= -1.; -// positions_diff[3].y *= -1.; -// } -// [ -// Vec2::new( -// uinode_rect.min.x + positions_diff[0].x, -// uinode_rect.min.y + positions_diff[0].y, -// ), -// Vec2::new( -// uinode_rect.max.x + positions_diff[1].x, -// uinode_rect.min.y + positions_diff[1].y, -// ), -// Vec2::new( -// uinode_rect.max.x + positions_diff[2].x, -// uinode_rect.max.y + positions_diff[2].y, -// ), -// Vec2::new( -// uinode_rect.min.x + positions_diff[3].x, -// uinode_rect.max.y + positions_diff[3].y, -// ), -// ] -// .map(|pos| pos / atlas_extent) -// }; - -// let color = extracted_uinode.color.as_linear_rgba_f32(); -// for i in QUAD_INDICES { -// ui_meta.vertices.push(UiVertex { -// position: positions_clipped[i].into(), -// uv: uvs[i].into(), -// color, -// mode, -// }); -// } - -// last_z = extracted_uinode.transform.w_axis[2]; -// end += QUAD_INDICES.len() as u32; -// } -// } - -// // if start != end, there is one last batch to process -// if start != end { -// commands.spawn(UiBatch { -// range: start..end, -// image: current_batch_image, -// z: last_z, -// }); -// } - -// ui_meta.vertices.write_buffer(&render_device, &render_queue); - -// extracted_uinodes.clear(); -// ======= let draw_function = draw_functions.read().id::(); + extracted_uinodes.ranges.sort_by_key(|extracted_range| extracted_range.stack_index); for (view, mut transparent_phase) in &mut views { let pipeline = pipelines.specialize( &pipeline_cache, &ui_pipeline, UiPipelineKey { hdr: view.hdr }, ); + transparent_phase .items .reserve(extracted_uinodes.ranges.len()); - for (entity, extracted_range) in extracted_uinodes.ranges.iter() { + for extracted_range in extracted_uinodes.ranges.iter() { transparent_phase.add(TransparentUi { draw_function, pipeline, - entity: *entity, - sort_key: extracted_range.stack_index, + entity: commands.spawn_empty().id(), + //sort_key: extracted_range.stack_index, // batch_size will be calculated in prepare_uinodes batch_size: 0, }); @@ -919,10 +759,8 @@ pub fn prepare_uinodes( for mut ui_phase in &mut phases { let mut batch_item_index = 0; let mut batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); - - for item_index in 0..ui_phase.items.len() { - let item = &mut ui_phase.items[item_index]; - if let Some(extracted_range) = extracted_uinodes.ranges.get(item.entity) { + for (n, extracted_range) in extracted_uinodes.ranges.iter().enumerate() { + for node_index in extracted_range.range.start..extracted_range.range.end { let extracted_uinode = &extracted_uinodes.uinodes[node_index as usize]; @@ -932,7 +770,7 @@ pub fn prepare_uinodes( if existing_batch.is_none() { if let Some(gpu_image) = gpu_images.get(&extracted_uinode.image) { - batch_item_index = item_index; + batch_item_index = n; batch_image_handle = extracted_uinode.image.id(); let new_batch = UiBatch { @@ -940,7 +778,8 @@ pub fn prepare_uinodes( image_handle_id: extracted_uinode.image.id(), }; - batches.push((item.entity, new_batch)); + batches.push((ui_phase.items[n].entity, new_batch)); + image_bind_groups .values @@ -1088,14 +927,12 @@ pub fn prepare_uinodes( existing_batch.unwrap().1.range.end = index; } ui_phase.items[batch_item_index].batch_size += 1; - } else { - batch_image_handle = HandleId::Id(Uuid::nil(), u64::MAX); - } - } + } } ui_meta.vertices.write_buffer(&render_device, &render_queue); *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } + extracted_uinodes.ranges.clear(); extracted_uinodes.uinodes.clear(); } diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index d4b85ccdf7c65..9b48a91fc1adc 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -86,7 +86,6 @@ impl Node for UiPassNode { } pub struct TransparentUi { - pub sort_key: u32, pub entity: Entity, pub pipeline: CachedRenderPipelineId, pub draw_function: DrawFunctionId, @@ -94,7 +93,7 @@ pub struct TransparentUi { } impl PhaseItem for TransparentUi { - type SortKey = u32; + type SortKey = (); #[inline] fn entity(&self) -> Entity { @@ -103,7 +102,7 @@ impl PhaseItem for TransparentUi { #[inline] fn sort_key(&self) -> Self::SortKey { - self.sort_key + () } #[inline] @@ -112,8 +111,7 @@ impl PhaseItem for TransparentUi { } #[inline] - fn sort(items: &mut [Self]) { - items.sort_by_key(|item| item.sort_key()); + fn sort(_items: &mut [Self]) { } #[inline] From 68b079449fee3f176d36e6dbebf2b46ab3c01a20 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 15:56:31 +0100 Subject: [PATCH 42/60] Removed unused. --- crates/bevy_ui/src/render/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 8fd38ea0b37de..40c5443c512e3 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -2,7 +2,6 @@ mod pipeline; mod render_pass; use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d}; -use bevy_ecs::storage::SparseSet; use bevy_hierarchy::Parent; use bevy_render::{ExtractSchedule, Render}; use bevy_window::{PrimaryWindow, Window}; @@ -308,7 +307,6 @@ fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) } pub fn extract_uinode_borders( - mut commands: Commands, mut extracted_uinodes: ResMut, windows: Extract>>, ui_scale: Extract>, @@ -427,7 +425,6 @@ pub fn extract_uinodes( uinode_query: Extract< Query< ( - Entity, &UiStackIndex, &Node, &GlobalTransform, @@ -440,7 +437,7 @@ pub fn extract_uinodes( >, >, ) { - for (entity, stack_index, uinode, transform, color, maybe_image, visibility, clip) in + for (stack_index, uinode, transform, color, maybe_image, visibility, clip) in uinode_query.iter() { // Skip invisible and completely transparent nodes @@ -552,7 +549,6 @@ pub fn extract_default_ui_camera_view( #[cfg(feature = "bevy_text")] pub fn extract_text_uinodes( - mut commands: Commands, mut extracted_uinodes: ResMut, texture_atlases: Extract>>, windows: Extract>>, @@ -691,7 +687,7 @@ pub fn queue_uinodes( transparent_phase .items .reserve(extracted_uinodes.ranges.len()); - for extracted_range in extracted_uinodes.ranges.iter() { + for _extracted_range in extracted_uinodes.ranges.iter() { transparent_phase.add(TransparentUi { draw_function, pipeline, @@ -949,6 +945,5 @@ pub fn prepare_uinodes( *previous_len = batches.len(); commands.insert_or_spawn_batch(batches); } - extracted_uinodes.ranges.clear(); - extracted_uinodes.uinodes.clear(); + extracted_uinodes.clear(); } From c26fc596499da4a83f85a1aaadc56c1573816517 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 18:29:47 +0100 Subject: [PATCH 43/60] removed commented out line --- crates/bevy_ui/src/render/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 40c5443c512e3..9379cfa202b87 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -692,7 +692,6 @@ pub fn queue_uinodes( draw_function, pipeline, entity: commands.spawn_empty().id(), - //sort_key: extracted_range.stack_index, // batch_size will be calculated in prepare_uinodes batch_size: 0, }); From cc570e565fabbe2a9de59ad1a815daf016ef37ec Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 18:38:39 +0100 Subject: [PATCH 44/60] removed `queue_uinodes` function --- crates/bevy_ui/src/render/mod.rs | 56 +++++++++++++++----------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 9379cfa202b87..3a95a8a09d720 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -94,7 +94,6 @@ pub fn build_ui_render(app: &mut App) { .add_systems( Render, ( - queue_uinodes.in_set(RenderSet::Queue), sort_phase_system::.in_set(RenderSet::PhaseSort), prepare_uinodes.in_set(RenderSet::PrepareBindGroups), ), @@ -663,21 +662,39 @@ pub struct UiBatch { const TEXTURED_QUAD: u32 = 0; const UNTEXTURED_QUAD: u32 = 1; +#[derive(Resource, Default)] +pub struct UiImageBindGroups { + pub values: HashMap, BindGroup>, +} + #[allow(clippy::too_many_arguments)] -pub fn queue_uinodes( +pub fn prepare_uinodes( + mut commands: Commands, + render_device: Res, + render_queue: Res, + mut ui_meta: ResMut, mut extracted_uinodes: ResMut, + view_uniforms: Res, ui_pipeline: Res, - mut pipelines: ResMut>, - mut views: Query<(&ExtractedView, &mut RenderPhase)>, + mut image_bind_groups: ResMut, + gpu_images: Res>, + events: Res, + mut previous_len: Local, pipeline_cache: Res, draw_functions: Res>, - mut commands: Commands, + mut render_phases: ParamSet<( + Query<&mut RenderPhase>, + Query<(&ExtractedView, &mut RenderPhase)>, + )>, + mut pipelines: ResMut>, ) { let draw_function = draw_functions.read().id::(); + extracted_uinodes .ranges .sort_by_key(|extracted_range| extracted_range.stack_index); - for (view, mut transparent_phase) in &mut views { + + for (view, mut transparent_phase) in &mut render_phases.p1() { let pipeline = pipelines.specialize( &pipeline_cache, &ui_pipeline, @@ -687,38 +704,17 @@ pub fn queue_uinodes( transparent_phase .items .reserve(extracted_uinodes.ranges.len()); + for _extracted_range in extracted_uinodes.ranges.iter() { transparent_phase.add(TransparentUi { draw_function, pipeline, entity: commands.spawn_empty().id(), - // batch_size will be calculated in prepare_uinodes batch_size: 0, }); } } -} - -#[derive(Resource, Default)] -pub struct UiImageBindGroups { - pub values: HashMap, BindGroup>, -} -#[allow(clippy::too_many_arguments)] -pub fn prepare_uinodes( - mut commands: Commands, - render_device: Res, - render_queue: Res, - mut ui_meta: ResMut, - mut extracted_uinodes: ResMut, - view_uniforms: Res, - ui_pipeline: Res, - mut image_bind_groups: ResMut, - gpu_images: Res>, - mut phases: Query<&mut RenderPhase>, - events: Res, - mut previous_len: Local, -) { // If an image has changed, the GpuImage has (probably) changed for event in &events.images { match event { @@ -730,6 +726,8 @@ pub fn prepare_uinodes( } if let Some(view_binding) = view_uniforms.uniforms.binding() { + + let mut batches: Vec<(Entity, UiBatch)> = Vec::with_capacity(*previous_len); ui_meta.vertices.clear(); @@ -744,7 +742,7 @@ pub fn prepare_uinodes( // Vertex buffer index let mut index = 0; - for mut ui_phase in &mut phases { + for mut ui_phase in &mut render_phases.p0() { let mut batch_item_index = 0; let mut batch_image_handle = DEFAULT_IMAGE_HANDLE.id(); From 183fb5c0e4485aa7f34bdcbd131516a0f2989805 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 21:18:15 +0100 Subject: [PATCH 45/60] Added a background image to some of the buttons in the `many_buttons` example --- examples/stress_tests/many_buttons.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/stress_tests/many_buttons.rs b/examples/stress_tests/many_buttons.rs index 9cd125ac0d0dc..80674779b084d 100644 --- a/examples/stress_tests/many_buttons.rs +++ b/examples/stress_tests/many_buttons.rs @@ -73,9 +73,10 @@ fn button_system( } } -fn setup(mut commands: Commands) { +fn setup(mut commands: Commands, assets: Res) { warn!(include_str!("warning_string.txt")); - + + let image = assets.load("branding/icon.png"); let count = ROW_COLUMN_COUNT; let count_f = count as f32; let as_rainbow = |i: usize| Color::hsl((i as f32 / count_f) * 360.0, 0.9, 0.8); @@ -108,6 +109,7 @@ fn setup(mut commands: Commands) { spawn_text, border, border_color, + &image, ); } } @@ -124,6 +126,7 @@ fn spawn_button( spawn_text: bool, border: UiRect, border_color: BorderColor, + image: &Handle, ) { let width = 90.0 / total; let mut builder = commands.spawn(( @@ -145,6 +148,10 @@ fn spawn_button( IdleColor(background_color), )); + if (i + j) % 4 == 0 { + builder.insert(UiImage::new(image.clone())); + } + if spawn_text { builder.with_children(|commands| { commands.spawn(TextBundle::from_section( From 0def05bcf7add291050ed368e8da8c99f3669971 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 22:20:43 +0100 Subject: [PATCH 46/60] Added an image to some of the buttons in the `many_buttons` example. --- examples/stress_tests/many_buttons.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/examples/stress_tests/many_buttons.rs b/examples/stress_tests/many_buttons.rs index 80674779b084d..0d8f264155f8d 100644 --- a/examples/stress_tests/many_buttons.rs +++ b/examples/stress_tests/many_buttons.rs @@ -5,6 +5,9 @@ //! //! //! To start the demo without borders run //! `cargo run --example many_buttons --release no-borders` +//! +//! To start the demo without images run +//! `cargo run --example many_buttons --release no-images` //! //| To do a full layout update each frame run //! `cargo run --example many_buttons --release recompute-layout` @@ -75,8 +78,13 @@ fn button_system( fn setup(mut commands: Commands, assets: Res) { warn!(include_str!("warning_string.txt")); - - let image = assets.load("branding/icon.png"); + let image = + if !std::env::args().any(|arg| arg == "no-images") { + Some(assets.load("branding/icon.png")) + } else { + None + }; + let count = ROW_COLUMN_COUNT; let count_f = count as f32; let as_rainbow = |i: usize| Color::hsl((i as f32 / count_f) * 360.0, 0.9, 0.8); @@ -109,7 +117,7 @@ fn setup(mut commands: Commands, assets: Res) { spawn_text, border, border_color, - &image, + image.as_ref(), ); } } @@ -126,7 +134,7 @@ fn spawn_button( spawn_text: bool, border: UiRect, border_color: BorderColor, - image: &Handle, + image: Option<&Handle>, ) { let width = 90.0 / total; let mut builder = commands.spawn(( @@ -148,8 +156,10 @@ fn spawn_button( IdleColor(background_color), )); - if (i + j) % 4 == 0 { - builder.insert(UiImage::new(image.clone())); + if let Some(image) = image { + if (i + j) % 4 == 0 { + builder.insert(UiImage::new(image.clone())); + } } if spawn_text { From 0e535a18ed19322b5c0eb4e246ac972be7e3a8ae Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 29 Aug 2023 22:22:13 +0100 Subject: [PATCH 47/60] cargo fmt --- examples/stress_tests/many_buttons.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/stress_tests/many_buttons.rs b/examples/stress_tests/many_buttons.rs index 0d8f264155f8d..a54576ca7807b 100644 --- a/examples/stress_tests/many_buttons.rs +++ b/examples/stress_tests/many_buttons.rs @@ -5,7 +5,7 @@ //! //! //! To start the demo without borders run //! `cargo run --example many_buttons --release no-borders` -//! +//! //! To start the demo without images run //! `cargo run --example many_buttons --release no-images` //! @@ -78,13 +78,12 @@ fn button_system( fn setup(mut commands: Commands, assets: Res) { warn!(include_str!("warning_string.txt")); - let image = - if !std::env::args().any(|arg| arg == "no-images") { - Some(assets.load("branding/icon.png")) - } else { - None - }; - + let image = if !std::env::args().any(|arg| arg == "no-images") { + Some(assets.load("branding/icon.png")) + } else { + None + }; + let count = ROW_COLUMN_COUNT; let count_f = count as f32; let as_rainbow = |i: usize| Color::hsl((i as f32 / count_f) * 360.0, 0.9, 0.8); From d8efd2114ffc0d404f8688f736b001becc218cf8 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 30 Aug 2023 15:42:31 +0100 Subject: [PATCH 48/60] added many_uinodes example --- Cargo.toml | 13 ++ examples/README.md | 1 + examples/stress_tests/many_uinodes.rs | 173 ++++++++++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 examples/stress_tests/many_uinodes.rs diff --git a/Cargo.toml b/Cargo.toml index 35a675b62f244..069f7bd6db020 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1788,6 +1788,19 @@ description = "Test rendering of many UI elements" category = "Stress Tests" wasm = true + +[[example]] +name = "many_uinodes" +path = "examples/stress_tests/many_uinodes.rs" +doc-scrape-examples = true + +[package.metadata.example.many_uinodes] +name = "Many UI nodes" +description = "Test rendering of many UI elements" +category = "Stress Tests" +wasm = true + + [[example]] name = "many_cubes" path = "examples/stress_tests/many_cubes.rs" diff --git a/examples/README.md b/examples/README.md index 6f53c34f7048c..fad2b3a9fd3c9 100644 --- a/examples/README.md +++ b/examples/README.md @@ -316,6 +316,7 @@ Example | Description [Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering. [Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights [Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites. +[Many UI nodes](../examples/stress_tests/many_uinodes.rs) | Test rendering of many UI elements [Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark [Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance diff --git a/examples/stress_tests/many_uinodes.rs b/examples/stress_tests/many_uinodes.rs new file mode 100644 index 0000000000000..7517e0183ca86 --- /dev/null +++ b/examples/stress_tests/many_uinodes.rs @@ -0,0 +1,173 @@ +//! This example shows what happens when there is a lot of UI nodes on screen. + +use bevy_internal::{ + render::{texture::DEFAULT_IMAGE_HANDLE, Extract, RenderApp}, + ui::{ExtractedUiNode, ExtractedUiNodes}, +}; + +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + prelude::*, + window::{PresentMode, WindowPlugin}, +}; +use rand::{seq::SliceRandom, Rng, SeedableRng}; + +fn main() { + let mut app = App::new(); + app.add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + resolution: (1024.0, 768.0).into(), + title: "many_uinodes".into(), + present_mode: PresentMode::AutoNoVsync, + ..default() + }), + ..default() + }), + FrameTimeDiagnosticsPlugin, + LogDiagnosticsPlugin::default(), + )) + .add_systems(Startup, setup); + + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + + render_app.add_systems( + ExtractSchedule, + ( + extract::, + extract::, + extract::, + extract::, + extract::, + ), + ); + + app.run(); +} + +#[derive(Component)] +pub struct A; + +#[derive(Component)] +pub struct B; + +#[derive(Component)] +pub struct C; + +#[derive(Component)] +pub struct D; + +#[derive(Component)] +pub struct E; + +fn extract( + mut extracted_uinodes: ResMut, + images: Extract>>, + uinode_query: Extract< + Query< + ( + Entity, + &StackIndex, + &Size, + &GlobalTransform, + &BackgroundColor, + Option<&UiImage>, + &ComputedVisibility, + ), + With, + >, + >, +) { + for (entity, stack_index, size, transform, color, maybe_image, visibility) in + uinode_query.iter() + { + // Skip invisible and completely transparent nodes + if !visibility.is_visible() || color.0.a() == 0.0 { + continue; + } + + let (image, flip_x, flip_y) = if let Some(image) = maybe_image { + // Skip loading images + if !images.contains(&image.texture) { + continue; + } + (image.texture.clone_weak(), image.flip_x, image.flip_y) + } else { + (DEFAULT_IMAGE_HANDLE.typed(), false, false) + }; + + extracted_uinodes.push_node( + stack_index.0 as u32, + ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: Rect {s + min: Vec2::ZERO, + max: size.0, + }, + clip: None, + image, + atlas_size: None, + flip_x, + flip_y, + }, + ); + } +} + +#[derive(Component)] +pub struct Size(Vec2); + +#[derive(Component)] +pub struct StackIndex(usize); + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(Camera2dBundle::default()); + + let image_handles = [ + "branding/bevy_logo_light.png", + "branding/bevy_logo_dark.png", + "branding/icon.png", + ]; + let colors = [ + Color::WHITE, + Color::RED, + Color::GREEN, + Color::BLUE, + Color::YELLOW, + ]; + let mut rng = rand::rngs::StdRng::seed_from_u64(42); + + for stack_index in 0..100_000 { + let w = rng.gen_range(10.0..150.0); + let h = rng.gen_range(10.0..150.0); + let x = rng.gen_range(0.0..1024.0); + let y = rng.gen_range(0.0..768.0); + let color = *colors.choose(&mut rng).unwrap(); + + let mut builder = commands.spawn(( + Size(Vec2::new(w, h)), + Transform::from_translation(Vec3::new(x, y, 1.0)), + GlobalTransform::default(), + StackIndex(stack_index), + BackgroundColor(color), + VisibilityBundle::default(), + )); + + if rng.gen_range(0..5) == 0 { + let image = image_handles.choose(&mut rng).unwrap(); + builder.insert(UiImage::new(asset_server.load(*image))); + } + + match rng.gen_range(0..5) { + 0 => builder.insert(A), + 1 => builder.insert(B), + 2 => builder.insert(C), + 3 => builder.insert(D), + _ => builder.insert(E), + }; + } +} From a9217343fc780143b172a1833539d97b7b221224 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Wed, 30 Aug 2023 21:01:50 +0100 Subject: [PATCH 49/60] added many_uinodes example --- examples/stress_tests/many_uinodes.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stress_tests/many_uinodes.rs b/examples/stress_tests/many_uinodes.rs index 7517e0183ca86..6d9976d2b7135 100644 --- a/examples/stress_tests/many_uinodes.rs +++ b/examples/stress_tests/many_uinodes.rs @@ -104,7 +104,7 @@ fn extract( ExtractedUiNode { transform: transform.compute_matrix(), color: color.0, - rect: Rect {s + rect: Rect { min: Vec2::ZERO, max: size.0, }, From 4aeb0b347cd191811524671c2b4af595fd32b398 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 31 Aug 2023 21:54:30 +0100 Subject: [PATCH 50/60] updated example --- Cargo.toml | 10 +- crates/bevy_ui/src/render/mod.rs | 2 - examples/README.md | 2 +- examples/stress_tests/many_rects.rs | 265 ++++++++++++++++++++++++++ examples/stress_tests/many_uinodes.rs | 173 ----------------- 5 files changed, 271 insertions(+), 181 deletions(-) create mode 100644 examples/stress_tests/many_rects.rs delete mode 100644 examples/stress_tests/many_uinodes.rs diff --git a/Cargo.toml b/Cargo.toml index 069f7bd6db020..67bdd2908a97f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1790,13 +1790,13 @@ wasm = true [[example]] -name = "many_uinodes" -path = "examples/stress_tests/many_uinodes.rs" +name = "many_rects" +path = "examples/stress_tests/many_rects.rs" doc-scrape-examples = true -[package.metadata.example.many_uinodes] -name = "Many UI nodes" -description = "Test rendering of many UI elements" +[package.metadata.example.many_rects] +name = "Many Rects" +description = "Benchmark to test UI rendering performance" category = "Stress Tests" wasm = true diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 3a95a8a09d720..e45312e2835eb 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -726,8 +726,6 @@ pub fn prepare_uinodes( } if let Some(view_binding) = view_uniforms.uniforms.binding() { - - let mut batches: Vec<(Entity, UiBatch)> = Vec::with_capacity(*previous_len); ui_meta.vertices.clear(); diff --git a/examples/README.md b/examples/README.md index fad2b3a9fd3c9..ac525b6e65987 100644 --- a/examples/README.md +++ b/examples/README.md @@ -315,8 +315,8 @@ Example | Description [Many Gizmos](../examples/stress_tests/many_gizmos.rs) | Test rendering of many gizmos [Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering. [Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights +[Many Rects](../examples/stress_tests/many_rects.rs) | Benchmark to test UI rendering performance [Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites. -[Many UI nodes](../examples/stress_tests/many_uinodes.rs) | Test rendering of many UI elements [Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark [Transform Hierarchy](../examples/stress_tests/transform_hierarchy.rs) | Various test cases for hierarchy and transform propagation performance diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs new file mode 100644 index 0000000000000..be4b8cbc3b971 --- /dev/null +++ b/examples/stress_tests/many_rects.rs @@ -0,0 +1,265 @@ +//! Draws a mix of approximately `100_000` textured and untextured rectangles on screen using the UI's renderer. +//! +//! This example doesn't spawn UI node bundles and instead add its own custom extraction functions to the `ExtractSchedule`. +//! This bypasses the layout systems so that only the UI's rendering systems are put under stress. +//! +//! To run the demo with extraction iterating the UI stack use: +//! `cargo run --example many_rects --release iter-stack` +//! +use bevy_internal::{ + render::{texture::DEFAULT_IMAGE_HANDLE, Extract, RenderApp}, + ui::{ExtractedUiNode, ExtractedUiNodes}, +}; + +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + prelude::*, + window::{PresentMode, WindowPlugin}, +}; +use rand::{seq::SliceRandom, Rng, SeedableRng}; + +const SEED: u64 = 42; +const MIN_EDGE: f32 = 10.; +const MAX_EDGE: f32 = 150.; +const WIDTH: f32 = 1024.; +const HEIGHT: f32 = 768.; +const STACK_SIZE: usize = 10000; +const TEXTURED_RATIO: f32 = 0.2; + +#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] +struct ExtractRect; + +fn main() { + let mut app = App::new(); + app.add_plugins(( + DefaultPlugins.set(WindowPlugin { + primary_window: Some(Window { + resolution: (WIDTH, HEIGHT).into(), + title: "many_rects".into(), + present_mode: PresentMode::AutoNoVsync, + ..default() + }), + ..default() + }), + FrameTimeDiagnosticsPlugin, + LogDiagnosticsPlugin::default(), + )) + .add_systems(Startup, setup); + + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + + if std::env::args().any(|arg| arg == "iter-stack") { + render_app.add_systems( + ExtractSchedule, + ( + extract_rect_iter_stack::<1>, + extract_rect_iter_stack::<2>, + extract_rect_iter_stack::<4>, + extract_rect_iter_stack::<8>, + extract_rect_iter_stack::<16>, + extract_rect_iter_stack::<32>, + ) + .chain(), + ); + } else { + render_app.add_systems( + ExtractSchedule, + ( + extract_rect::<1>, + extract_rect::<2>, + extract_rect::<4>, + extract_rect::<8>, + extract_rect::<16>, + extract_rect::<32>, + ) + .chain(), + ); + } + + app.run(); +} + +#[derive(Component)] +pub struct ExtractionMarker; + +#[derive(Resource, Deref, DerefMut)] +pub struct RectStack(Vec); + +fn extract_rect_iter_stack( + mut commands: Commands, + mut extracted_uinodes: ResMut, + images: Extract>>, + ui_stack: Extract>, + uinode_query: Extract< + Query< + ( + &Size, + &GlobalTransform, + &BackgroundColor, + Option<&UiImage>, + &ComputedVisibility, + ), + With>, + >, + >, +) { + for (stack_index, entity) in ui_stack.iter().enumerate() { + if let Ok((size, transform, color, maybe_image, visibility)) = uinode_query.get(*entity) { + // Skip invisible and completely transparent nodes + if !visibility.is_visible() || color.0.a() == 0.0 { + continue; + } + + let (image, flip_x, flip_y) = if let Some(image) = maybe_image { + // Skip loading images + if !images.contains(&image.texture) { + continue; + } + (image.texture.clone_weak(), image.flip_x, image.flip_y) + } else { + (DEFAULT_IMAGE_HANDLE.typed(), false, false) + }; + extracted_uinodes.push_nodes( + stack_index as u32, + (0..N).map(|_| ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: Rect { + min: Vec2::ZERO, + max: size.0, + }, + clip: None, + image: image.clone_weak(), + atlas_size: None, + flip_x, + flip_y, + }), + ); + } + } +} + +fn extract_rect( + mut commands: Commands, + mut extracted_uinodes: ResMut, + images: Extract>>, + uinode_query: Extract< + Query< + ( + Entity, + &StackIndex, + &Size, + &GlobalTransform, + &BackgroundColor, + Option<&UiImage>, + &ComputedVisibility, + ), + With>, + >, + >, +) { + for (entity, stack_index, size, transform, color, maybe_image, visibility) in + uinode_query.iter() + { + // Skip invisible and completely transparent nodes + if !visibility.is_visible() || color.0.a() == 0.0 { + continue; + } + + let (image, flip_x, flip_y) = if let Some(image) = maybe_image { + // Skip loading images + if !images.contains(&image.texture) { + continue; + } + (image.texture.clone_weak(), image.flip_x, image.flip_y) + } else { + (DEFAULT_IMAGE_HANDLE.typed(), false, false) + }; + extracted_uinodes.push_nodes( + stack_index.0 as u32, + (0..N).map(|_| ExtractedUiNode { + transform: transform.compute_matrix(), + color: color.0, + rect: Rect { + min: Vec2::ZERO, + max: size.0, + }, + clip: None, + image: image.clone_weak(), + atlas_size: None, + flip_x, + flip_y, + }), + ); + } +} + +#[derive(Component)] +pub struct Size(Vec2); + +#[derive(Component)] +pub struct StackIndex(usize); + +fn setup(mut commands: Commands, asset_server: Res) { + commands.spawn(Camera2dBundle::default()); + + let image_handles = [ + "branding/bevy_logo_light.png", + "branding/bevy_logo_dark.png", + "branding/icon.png", + ]; + let colors = [ + Color::WHITE, + Color::RED, + Color::GREEN, + Color::BLUE, + Color::YELLOW, + ]; + let mut rng = rand::rngs::StdRng::seed_from_u64(SEED); + let mut rect_stack = RectStack(Vec::with_capacity(STACK_SIZE)); + for _ in 0..STACK_SIZE { + let n = rng.gen_range(0..6); + let mut builder = match n { + 0 => commands.spawn(ExtractionMarker::<1>), + 1 => commands.spawn(ExtractionMarker::<2>), + 2 => commands.spawn(ExtractionMarker::<4>), + 3 => commands.spawn(ExtractionMarker::<8>), + 4 => commands.spawn(ExtractionMarker::<16>), + _ => commands.spawn(ExtractionMarker::<32>), + }; + if rng.gen::() <= TEXTURED_RATIO { + let image = image_handles.choose(&mut rng).unwrap(); + builder.insert(UiImage::new(asset_server.load(*image))); + } + rect_stack.push(builder.id()); + } + rect_stack.shuffle(&mut rng); + + let bundles: Vec<_> = rect_stack + .iter() + .enumerate() + .map(|(stack_index, entity)| { + (*entity, { + let w = rng.gen_range(MIN_EDGE..MAX_EDGE); + let h = rng.gen_range(MIN_EDGE..MAX_EDGE); + let x = rng.gen_range(0.0..WIDTH); + let y = rng.gen_range(0.0..HEIGHT); + let color = *colors.choose(&mut rng).unwrap(); + ( + Size(Vec2::new(w, h)), + Transform::from_translation(Vec3::new(x, y, 1.0)), + GlobalTransform::default(), + StackIndex(stack_index), + BackgroundColor(color), + VisibilityBundle::default(), + ) + }) + }) + .collect(); + + commands.insert_or_spawn_batch(bundles); + commands.insert_resource(rect_stack); +} diff --git a/examples/stress_tests/many_uinodes.rs b/examples/stress_tests/many_uinodes.rs deleted file mode 100644 index 6d9976d2b7135..0000000000000 --- a/examples/stress_tests/many_uinodes.rs +++ /dev/null @@ -1,173 +0,0 @@ -//! This example shows what happens when there is a lot of UI nodes on screen. - -use bevy_internal::{ - render::{texture::DEFAULT_IMAGE_HANDLE, Extract, RenderApp}, - ui::{ExtractedUiNode, ExtractedUiNodes}, -}; - -use bevy::{ - diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, - prelude::*, - window::{PresentMode, WindowPlugin}, -}; -use rand::{seq::SliceRandom, Rng, SeedableRng}; - -fn main() { - let mut app = App::new(); - app.add_plugins(( - DefaultPlugins.set(WindowPlugin { - primary_window: Some(Window { - resolution: (1024.0, 768.0).into(), - title: "many_uinodes".into(), - present_mode: PresentMode::AutoNoVsync, - ..default() - }), - ..default() - }), - FrameTimeDiagnosticsPlugin, - LogDiagnosticsPlugin::default(), - )) - .add_systems(Startup, setup); - - let render_app = match app.get_sub_app_mut(RenderApp) { - Ok(render_app) => render_app, - Err(_) => return, - }; - - render_app.add_systems( - ExtractSchedule, - ( - extract::, - extract::, - extract::, - extract::, - extract::, - ), - ); - - app.run(); -} - -#[derive(Component)] -pub struct A; - -#[derive(Component)] -pub struct B; - -#[derive(Component)] -pub struct C; - -#[derive(Component)] -pub struct D; - -#[derive(Component)] -pub struct E; - -fn extract( - mut extracted_uinodes: ResMut, - images: Extract>>, - uinode_query: Extract< - Query< - ( - Entity, - &StackIndex, - &Size, - &GlobalTransform, - &BackgroundColor, - Option<&UiImage>, - &ComputedVisibility, - ), - With, - >, - >, -) { - for (entity, stack_index, size, transform, color, maybe_image, visibility) in - uinode_query.iter() - { - // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { - continue; - } - - let (image, flip_x, flip_y) = if let Some(image) = maybe_image { - // Skip loading images - if !images.contains(&image.texture) { - continue; - } - (image.texture.clone_weak(), image.flip_x, image.flip_y) - } else { - (DEFAULT_IMAGE_HANDLE.typed(), false, false) - }; - - extracted_uinodes.push_node( - stack_index.0 as u32, - ExtractedUiNode { - transform: transform.compute_matrix(), - color: color.0, - rect: Rect { - min: Vec2::ZERO, - max: size.0, - }, - clip: None, - image, - atlas_size: None, - flip_x, - flip_y, - }, - ); - } -} - -#[derive(Component)] -pub struct Size(Vec2); - -#[derive(Component)] -pub struct StackIndex(usize); - -fn setup(mut commands: Commands, asset_server: Res) { - commands.spawn(Camera2dBundle::default()); - - let image_handles = [ - "branding/bevy_logo_light.png", - "branding/bevy_logo_dark.png", - "branding/icon.png", - ]; - let colors = [ - Color::WHITE, - Color::RED, - Color::GREEN, - Color::BLUE, - Color::YELLOW, - ]; - let mut rng = rand::rngs::StdRng::seed_from_u64(42); - - for stack_index in 0..100_000 { - let w = rng.gen_range(10.0..150.0); - let h = rng.gen_range(10.0..150.0); - let x = rng.gen_range(0.0..1024.0); - let y = rng.gen_range(0.0..768.0); - let color = *colors.choose(&mut rng).unwrap(); - - let mut builder = commands.spawn(( - Size(Vec2::new(w, h)), - Transform::from_translation(Vec3::new(x, y, 1.0)), - GlobalTransform::default(), - StackIndex(stack_index), - BackgroundColor(color), - VisibilityBundle::default(), - )); - - if rng.gen_range(0..5) == 0 { - let image = image_handles.choose(&mut rng).unwrap(); - builder.insert(UiImage::new(asset_server.load(*image))); - } - - match rng.gen_range(0..5) { - 0 => builder.insert(A), - 1 => builder.insert(B), - 2 => builder.insert(C), - 3 => builder.insert(D), - _ => builder.insert(E), - }; - } -} From 42436a03405c4431c78662b88f2a4404162aa233 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Thu, 31 Aug 2023 22:20:08 +0100 Subject: [PATCH 51/60] update example --- examples/stress_tests/many_rects.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index be4b8cbc3b971..ce2f4c28fa982 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -23,7 +23,7 @@ const MIN_EDGE: f32 = 10.; const MAX_EDGE: f32 = 150.; const WIDTH: f32 = 1024.; const HEIGHT: f32 = 768.; -const STACK_SIZE: usize = 10000; +const STACK_SIZE: usize = 33000; const TEXTURED_RATIO: f32 = 0.2; #[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)] @@ -221,13 +221,13 @@ fn setup(mut commands: Commands, asset_server: Res) { let mut rng = rand::rngs::StdRng::seed_from_u64(SEED); let mut rect_stack = RectStack(Vec::with_capacity(STACK_SIZE)); for _ in 0..STACK_SIZE { - let n = rng.gen_range(0..6); + let n = rng.gen_range(0..63); let mut builder = match n { - 0 => commands.spawn(ExtractionMarker::<1>), - 1 => commands.spawn(ExtractionMarker::<2>), - 2 => commands.spawn(ExtractionMarker::<4>), - 3 => commands.spawn(ExtractionMarker::<8>), - 4 => commands.spawn(ExtractionMarker::<16>), + 0..= 31 => commands.spawn(ExtractionMarker::<1>), + 32..= 47 => commands.spawn(ExtractionMarker::<2>), + 48..= 55 => commands.spawn(ExtractionMarker::<4>), + 56..= 59 => commands.spawn(ExtractionMarker::<8>), + 60..= 61 => commands.spawn(ExtractionMarker::<16>), _ => commands.spawn(ExtractionMarker::<32>), }; if rng.gen::() <= TEXTURED_RATIO { From 7476d1bd033b486224171c3f4734e8964b4df43b Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 07:37:30 +0100 Subject: [PATCH 52/60] Sort extracted nodes in queue --- crates/bevy_ui/src/render/mod.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index e45312e2835eb..3487752d044d7 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -94,6 +94,7 @@ pub fn build_ui_render(app: &mut App) { .add_systems( Render, ( + queue_uinodes.in_set(RenderSet::Queue), sort_phase_system::.in_set(RenderSet::PhaseSort), prepare_uinodes.in_set(RenderSet::PrepareBindGroups), ), @@ -667,6 +668,14 @@ pub struct UiImageBindGroups { pub values: HashMap, BindGroup>, } +pub fn queue_uinodes( + mut extracted_uinodes: ResMut, +) { + extracted_uinodes + .ranges + .sort_by_key(|extracted_range| extracted_range.stack_index); +} + #[allow(clippy::too_many_arguments)] pub fn prepare_uinodes( mut commands: Commands, @@ -690,9 +699,6 @@ pub fn prepare_uinodes( ) { let draw_function = draw_functions.read().id::(); - extracted_uinodes - .ranges - .sort_by_key(|extracted_range| extracted_range.stack_index); for (view, mut transparent_phase) in &mut render_phases.p1() { let pipeline = pipelines.specialize( From 54bf3757d812b832253b525edf945e90d9b07202 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 10:42:15 +0100 Subject: [PATCH 53/60] use unstable sort --- crates/bevy_ui/src/render/mod.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 3487752d044d7..1316184243891 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -165,7 +165,7 @@ pub struct ExtractedUiNode { } pub struct ExtractedRange { - stack_index: u32, + sort_key: u32, range: Range, } @@ -181,13 +181,23 @@ pub struct ExtractedUiNodes { //ranges: Vec, pub ranges: Vec, uinodes: Vec, + discriminator: u8, } impl ExtractedUiNodes { + pub fn finish(&mut self) { + self.discriminator += 1; + } + + #[inline] + pub fn get_key(&self, stack_index: u32) -> u32 { + (stack_index << 8) | (self.discriminator as u32) + } + /// Add a single `ExtractedUiNode` for rendering. pub fn push_node(&mut self, stack_index: u32, item: ExtractedUiNode) { self.ranges.push(ExtractedRange { - stack_index, + sort_key: self.get_key(stack_index), range: self.uinodes.len() as u32..(self.uinodes.len() + 1) as u32, }); self.uinodes.push(item); @@ -198,7 +208,7 @@ impl ExtractedUiNodes { let start = self.uinodes.len() as u32; self.uinodes.extend(items); self.ranges.push(ExtractedRange { - stack_index, + sort_key: self.get_key(stack_index), range: start..self.uinodes.len() as u32, }); } @@ -673,7 +683,9 @@ pub fn queue_uinodes( ) { extracted_uinodes .ranges - .sort_by_key(|extracted_range| extracted_range.stack_index); + .sort_unstable_by_key(|extracted_range| extracted_range.sort_key); + + extracted_uinodes.discriminator = 0; } #[allow(clippy::too_many_arguments)] From 9d714ebf374feb5a682e144ce4495e8c68c7065c Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 10:46:08 +0100 Subject: [PATCH 54/60] call finish() for extraction --- crates/bevy_ui/src/render/mod.rs | 4 ++++ examples/stress_tests/many_rects.rs | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 1316184243891..7f2e1ac5c2519 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -302,6 +302,7 @@ pub fn extract_atlas_uinodes( }, ); } + extracted_uinodes.finish(); } fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 { @@ -427,6 +428,7 @@ pub fn extract_uinode_borders( }), ); } + extracted_uinodes.finish(); } pub fn extract_uinodes( @@ -482,6 +484,7 @@ pub fn extract_uinodes( }, ); } + extracted_uinodes.finish(); } /// The UI camera is "moved back" by this many units (plus the [`UI_CAMERA_TRANSFORM_OFFSET`]) and also has a view @@ -629,6 +632,7 @@ pub fn extract_text_uinodes( ), ); } + extracted_uinodes.finish(); } #[repr(C)] diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index ce2f4c28fa982..488495b293bf7 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -89,7 +89,6 @@ pub struct ExtractionMarker; pub struct RectStack(Vec); fn extract_rect_iter_stack( - mut commands: Commands, mut extracted_uinodes: ResMut, images: Extract>>, ui_stack: Extract>, @@ -140,16 +139,15 @@ fn extract_rect_iter_stack( ); } } + extracted_uinodes.finish(); } fn extract_rect( - mut commands: Commands, mut extracted_uinodes: ResMut, images: Extract>>, uinode_query: Extract< Query< ( - Entity, &StackIndex, &Size, &GlobalTransform, @@ -161,7 +159,7 @@ fn extract_rect( >, >, ) { - for (entity, stack_index, size, transform, color, maybe_image, visibility) in + for (stack_index, size, transform, color, maybe_image, visibility) in uinode_query.iter() { // Skip invisible and completely transparent nodes @@ -195,6 +193,7 @@ fn extract_rect( }), ); } + extracted_uinodes.finish(); } #[derive(Component)] From 4f68ea4aeab5adee5faeb6c88e4c79b45396ad47 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 21:50:28 +0100 Subject: [PATCH 55/60] Clean up and improved `ExtractedUiNodes` interface. --- crates/bevy_ui/src/render/mod.rs | 137 +++++++++++++++------------- examples/stress_tests/many_rects.rs | 19 ++-- 2 files changed, 82 insertions(+), 74 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 7f2e1ac5c2519..8d63bdd48980c 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -178,45 +178,61 @@ impl ExtractedRange { #[derive(Resource, Default)] pub struct ExtractedUiNodes { - //ranges: Vec, - pub ranges: Vec, + ranges: Vec, uinodes: Vec, discriminator: u8, } -impl ExtractedUiNodes { - pub fn finish(&mut self) { - self.discriminator += 1; - } +pub struct UiExtractionBuffer<'a>(&'a mut ExtractedUiNodes); +impl UiExtractionBuffer<'_> { #[inline] - pub fn get_key(&self, stack_index: u32) -> u32 { - (stack_index << 8) | (self.discriminator as u32) + fn get_key(&self, stack_index: u32) -> u32 { + (stack_index << 8) | (self.0.discriminator as u32) } - + /// Add a single `ExtractedUiNode` for rendering. - pub fn push_node(&mut self, stack_index: u32, item: ExtractedUiNode) { - self.ranges.push(ExtractedRange { + pub fn push(&mut self, stack_index: u32, item: ExtractedUiNode) { + let start = self.0.uinodes.len() as u32; + self.0.ranges.push(ExtractedRange { sort_key: self.get_key(stack_index), - range: self.uinodes.len() as u32..(self.uinodes.len() + 1) as u32, + range: start..start + 1, }); - self.uinodes.push(item); + self.0.uinodes.push(item); } /// Add multiple `ExtractedUiNode`s for rendering. - pub fn push_nodes(&mut self, stack_index: u32, items: impl Iterator) { - let start = self.uinodes.len() as u32; - self.uinodes.extend(items); - self.ranges.push(ExtractedRange { + pub fn extend(&mut self, stack_index: u32, items: impl Iterator) { + let start = self.0.uinodes.len() as u32; + self.0.uinodes.extend(items); + self.0.ranges.push(ExtractedRange { sort_key: self.get_key(stack_index), - range: start..self.uinodes.len() as u32, + range: start..self.0.uinodes.len() as u32, }); } +} + +impl Drop for UiExtractionBuffer<'_> { + fn drop(&mut self) { + self.0.discriminator += 1; + } +} + +impl ExtractedUiNodes { + pub fn get_buffer<'a>(&'a mut self) -> UiExtractionBuffer<'a> { + UiExtractionBuffer(self) + } + + #[inline] + fn get_key(&self, stack_index: u32) -> u32 { + (stack_index << 8) | (self.discriminator as u32) + } /// Clear the buffers fn clear(&mut self) { self.ranges.clear(); self.uinodes.clear(); + self.discriminator = 0; } } @@ -240,6 +256,8 @@ pub fn extract_atlas_uinodes( >, >, ) { + let mut extraction_buffer = extracted_uinodes.get_buffer(); + for ( stack_index, uinode, @@ -288,7 +306,7 @@ pub fn extract_atlas_uinodes( atlas_rect.max *= scale; atlas_size *= scale; - extracted_uinodes.push_node( + extraction_buffer.push( stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -302,7 +320,6 @@ pub fn extract_atlas_uinodes( }, ); } - extracted_uinodes.finish(); } fn resolve_border_thickness(value: Val, parent_width: f32, viewport_size: Vec2) -> f32 { @@ -338,6 +355,7 @@ pub fn extract_uinode_borders( >, node_query: Extract>, ) { + let mut extraction_buffer = extracted_uinodes.get_buffer(); let image = bevy_render::texture::DEFAULT_IMAGE_HANDLE.typed(); let ui_logical_viewport_size = windows @@ -405,7 +423,7 @@ pub fn extract_uinode_borders( ]; let transform = global_transform.compute_matrix(); - extracted_uinodes.push_nodes( + extraction_buffer.extend( stack_index.0, border_rects .into_iter() @@ -428,7 +446,6 @@ pub fn extract_uinode_borders( }), ); } - extracted_uinodes.finish(); } pub fn extract_uinodes( @@ -449,6 +466,7 @@ pub fn extract_uinodes( >, >, ) { + let mut extration_buffer = extracted_uinodes.get_buffer(); for (stack_index, uinode, transform, color, maybe_image, visibility, clip) in uinode_query.iter() { @@ -467,7 +485,7 @@ pub fn extract_uinodes( (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.push_node( + extration_buffer.push( stack_index.0, ExtractedUiNode { transform: transform.compute_matrix(), @@ -484,7 +502,6 @@ pub fn extract_uinodes( }, ); } - extracted_uinodes.finish(); } /// The UI camera is "moved back" by this many units (plus the [`UI_CAMERA_TRANSFORM_OFFSET`]) and also has a view @@ -578,6 +595,7 @@ pub fn extract_text_uinodes( )>, >, ) { + let mut extraction_buffer = extracted_uinodes.get_buffer(); // TODO: Support window-independent UI scale: https://github.com/bevyengine/bevy/issues/5621 let scale_factor = windows .get_single() @@ -599,7 +617,7 @@ pub fn extract_text_uinodes( let mut color = Color::WHITE; let mut current_section = usize::MAX; - extracted_uinodes.push_nodes( + extraction_buffer.extend( stack_index.0, text_layout_info.glyphs.iter().map( |PositionedGlyph { @@ -632,7 +650,6 @@ pub fn extract_text_uinodes( ), ); } - extracted_uinodes.finish(); } #[repr(C)] @@ -682,10 +699,8 @@ pub struct UiImageBindGroups { pub values: HashMap, BindGroup>, } -pub fn queue_uinodes( - mut extracted_uinodes: ResMut, -) { - extracted_uinodes +pub fn queue_uinodes(mut extracted_uinodes: ResMut) { + extracted_uinodes .ranges .sort_unstable_by_key(|extracted_range| extracted_range.sort_key); @@ -715,7 +730,6 @@ pub fn prepare_uinodes( ) { let draw_function = draw_functions.read().id::(); - for (view, mut transparent_phase) in &mut render_phases.p1() { let pipeline = pipelines.specialize( &pipeline_cache, @@ -723,18 +737,15 @@ pub fn prepare_uinodes( UiPipelineKey { hdr: view.hdr }, ); - transparent_phase - .items - .reserve(extracted_uinodes.ranges.len()); - - for _extracted_range in extracted_uinodes.ranges.iter() { - transparent_phase.add(TransparentUi { + transparent_phase.items.extend( + std::iter::repeat_with(|| TransparentUi { draw_function, pipeline, entity: commands.spawn_empty().id(), batch_size: 0, - }); - } + }) + .take(extracted_uinodes.ranges.len()), + ); } // If an image has changed, the GpuImage has (probably) changed @@ -747,6 +758,28 @@ pub fn prepare_uinodes( }; } + if let Some(gpu_image) = gpu_images.get(&DEFAULT_IMAGE_HANDLE.typed()) { + image_bind_groups + .values + .entry(Handle::weak(DEFAULT_IMAGE_HANDLE.id())) + .or_insert_with(|| { + render_device.create_bind_group(&BindGroupDescriptor { + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView(&gpu_image.texture_view), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&gpu_image.sampler), + }, + ], + label: Some("ui_material_bind_group"), + layout: &ui_pipeline.image_layout, + }) + }); + } + if let Some(view_binding) = view_uniforms.uniforms.binding() { let mut batches: Vec<(Entity, UiBatch)> = Vec::with_capacity(*previous_len); @@ -766,32 +799,8 @@ pub fn prepare_uinodes( let mut batch_item_index = 0; let mut batch_image_handle = DEFAULT_IMAGE_HANDLE.id(); - if let Some(gpu_image) = gpu_images.get(&DEFAULT_IMAGE_HANDLE.typed()) { - image_bind_groups - .values - .entry(Handle::weak(DEFAULT_IMAGE_HANDLE.id())) - .or_insert_with(|| { - render_device.create_bind_group(&BindGroupDescriptor { - entries: &[ - BindGroupEntry { - binding: 0, - resource: BindingResource::TextureView(&gpu_image.texture_view), - }, - BindGroupEntry { - binding: 1, - resource: BindingResource::Sampler(&gpu_image.sampler), - }, - ], - label: Some("ui_material_bind_group"), - layout: &ui_pipeline.image_layout, - }) - }); - } - for (n, extracted_range) in extracted_uinodes.ranges.iter().enumerate() { - for node_index in extracted_range.range.start..extracted_range.range.end { - let extracted_uinode = &extracted_uinodes.uinodes[node_index as usize]; - + for extracted_uinode in &extracted_uinodes.uinodes[extracted_range.range()] { let existing_batch = if extracted_uinode.image.id() == DEFAULT_IMAGE_HANDLE.id() || extracted_uinode.image.id() == batch_image_handle { diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index 488495b293bf7..cef81374536bb 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -105,6 +105,7 @@ fn extract_rect_iter_stack( >, >, ) { + let mut extraction_buffer = extracted_uinodes.get_buffer(); for (stack_index, entity) in ui_stack.iter().enumerate() { if let Ok((size, transform, color, maybe_image, visibility)) = uinode_query.get(*entity) { // Skip invisible and completely transparent nodes @@ -139,7 +140,6 @@ fn extract_rect_iter_stack( ); } } - extracted_uinodes.finish(); } fn extract_rect( @@ -159,9 +159,8 @@ fn extract_rect( >, >, ) { - for (stack_index, size, transform, color, maybe_image, visibility) in - uinode_query.iter() - { + let mut extraction_buffer = extracted_uinodes.get_buffer(); + for (stack_index, size, transform, color, maybe_image, visibility) in uinode_query.iter() { // Skip invisible and completely transparent nodes if !visibility.is_visible() || color.0.a() == 0.0 { continue; @@ -178,7 +177,7 @@ fn extract_rect( }; extracted_uinodes.push_nodes( stack_index.0 as u32, - (0..N).map(|_| ExtractedUiNode { + (0..N).map(|_| extendUiNode { transform: transform.compute_matrix(), color: color.0, rect: Rect { @@ -222,11 +221,11 @@ fn setup(mut commands: Commands, asset_server: Res) { for _ in 0..STACK_SIZE { let n = rng.gen_range(0..63); let mut builder = match n { - 0..= 31 => commands.spawn(ExtractionMarker::<1>), - 32..= 47 => commands.spawn(ExtractionMarker::<2>), - 48..= 55 => commands.spawn(ExtractionMarker::<4>), - 56..= 59 => commands.spawn(ExtractionMarker::<8>), - 60..= 61 => commands.spawn(ExtractionMarker::<16>), + 0..=31 => commands.spawn(ExtractionMarker::<1>), + 32..=47 => commands.spawn(ExtractionMarker::<2>), + 48..=55 => commands.spawn(ExtractionMarker::<4>), + 56..=59 => commands.spawn(ExtractionMarker::<8>), + 60..=61 => commands.spawn(ExtractionMarker::<16>), _ => commands.spawn(ExtractionMarker::<32>), }; if rng.gen::() <= TEXTURED_RATIO { From d86f7b653dceabcc832c83e7e9d2e420b416f4b8 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 23:31:58 +0100 Subject: [PATCH 56/60] ci fixes --- crates/bevy_ui/src/render/mod.rs | 7 +------ crates/bevy_ui/src/render/render_pass.rs | 4 +--- examples/stress_tests/many_rects.rs | 7 +++---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 8d63bdd48980c..983e78439908d 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -219,15 +219,10 @@ impl Drop for UiExtractionBuffer<'_> { } impl ExtractedUiNodes { - pub fn get_buffer<'a>(&'a mut self) -> UiExtractionBuffer<'a> { + pub fn get_buffer(&mut self) -> UiExtractionBuffer<'_> { UiExtractionBuffer(self) } - #[inline] - fn get_key(&self, stack_index: u32) -> u32 { - (stack_index << 8) | (self.discriminator as u32) - } - /// Clear the buffers fn clear(&mut self) { self.ranges.clear(); diff --git a/crates/bevy_ui/src/render/render_pass.rs b/crates/bevy_ui/src/render/render_pass.rs index bef8a53fb1c38..3c56db1fac5f4 100644 --- a/crates/bevy_ui/src/render/render_pass.rs +++ b/crates/bevy_ui/src/render/render_pass.rs @@ -101,9 +101,7 @@ impl PhaseItem for TransparentUi { } #[inline] - fn sort_key(&self) -> Self::SortKey { - () - } + fn sort_key(&self) -> Self::SortKey {} #[inline] fn draw_function(&self) -> DrawFunctionId { diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index cef81374536bb..2e6f9baa08e9c 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -122,7 +122,7 @@ fn extract_rect_iter_stack( } else { (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.push_nodes( + extraction_buffer.extend( stack_index as u32, (0..N).map(|_| ExtractedUiNode { transform: transform.compute_matrix(), @@ -175,9 +175,9 @@ fn extract_rect( } else { (DEFAULT_IMAGE_HANDLE.typed(), false, false) }; - extracted_uinodes.push_nodes( + extraction_buffer.extend( stack_index.0 as u32, - (0..N).map(|_| extendUiNode { + (0..N).map(|_| ExtractedUiNode { transform: transform.compute_matrix(), color: color.0, rect: Rect { @@ -192,7 +192,6 @@ fn extract_rect( }), ); } - extracted_uinodes.finish(); } #[derive(Component)] From eeadc8b552472c0285a5427459d46bda46a67642 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 23:44:19 +0100 Subject: [PATCH 57/60] cargo fmt --- crates/bevy_ui/src/render/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 1242faed995a1..4e4dbcc1de9bb 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -266,7 +266,7 @@ pub fn extract_atlas_uinodes( ) in uinode_query.iter() { // Skip invisible and completely transparent nodes - if !view_visibility.get() || color.0.a() == 0.0 { + if !view_visibility.get() || color.0.a() == 0.0 { continue; } From 55e5cc9c76eccafd970a1646e7595bc8c2a2cec7 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Fri, 1 Sep 2023 23:58:50 +0100 Subject: [PATCH 58/60] fixed example --- examples/stress_tests/many_rects.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index 2e6f9baa08e9c..22b764a3fbd34 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -99,7 +99,7 @@ fn extract_rect_iter_stack( &GlobalTransform, &BackgroundColor, Option<&UiImage>, - &ComputedVisibility, + &ViewVisibility, ), With>, >, @@ -109,7 +109,7 @@ fn extract_rect_iter_stack( for (stack_index, entity) in ui_stack.iter().enumerate() { if let Ok((size, transform, color, maybe_image, visibility)) = uinode_query.get(*entity) { // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { + if !visibility.get() || color.0.a() == 0.0 { continue; } @@ -153,7 +153,7 @@ fn extract_rect( &GlobalTransform, &BackgroundColor, Option<&UiImage>, - &ComputedVisibility, + &ViewVisibility, ), With>, >, @@ -162,7 +162,7 @@ fn extract_rect( let mut extraction_buffer = extracted_uinodes.get_buffer(); for (stack_index, size, transform, color, maybe_image, visibility) in uinode_query.iter() { // Skip invisible and completely transparent nodes - if !visibility.is_visible() || color.0.a() == 0.0 { + if !visibility.get() || color.0.a() == 0.0 { continue; } From 0daa6184365ce4a5ee129a110676d599f7bfff7a Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 2 Sep 2023 00:23:43 +0100 Subject: [PATCH 59/60] No need to set discriminator to 0, it's already done by clear() --- crates/bevy_ui/src/render/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 4e4dbcc1de9bb..a96aa947bd7ed 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -699,8 +699,6 @@ pub fn queue_uinodes(mut extracted_uinodes: ResMut) { extracted_uinodes .ranges .sort_unstable_by_key(|extracted_range| extracted_range.sort_key); - - extracted_uinodes.discriminator = 0; } #[allow(clippy::too_many_arguments)] From f419f3d579a2debc9f3589d20d2f606ba5712e03 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Sat, 2 Sep 2023 02:04:35 +0100 Subject: [PATCH 60/60] fix for internal imports ci error --- examples/stress_tests/many_rects.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/stress_tests/many_rects.rs b/examples/stress_tests/many_rects.rs index 22b764a3fbd34..24b608b358eab 100644 --- a/examples/stress_tests/many_rects.rs +++ b/examples/stress_tests/many_rects.rs @@ -6,10 +6,8 @@ //! To run the demo with extraction iterating the UI stack use: //! `cargo run --example many_rects --release iter-stack` //! -use bevy_internal::{ - render::{texture::DEFAULT_IMAGE_HANDLE, Extract, RenderApp}, - ui::{ExtractedUiNode, ExtractedUiNodes}, -}; +use bevy::render::{texture::DEFAULT_IMAGE_HANDLE, Extract, RenderApp}; +use bevy::ui::{ExtractedUiNode, ExtractedUiNodes}; use bevy::{ diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},