Skip to content

Commit

Permalink
Extract UI nodes into a Vec (#17618)
Browse files Browse the repository at this point in the history
# Objective

Extract UI nodes into a `Vec` instead of an `EntityHashMap`.

## Solution

Extract UI nodes into a `Vec` instead of an `EntityHashMap`.
Store an index into the `Vec` in each transparent UI item.
Compare both the index and render entity in prepare so there aren't any
collisions.

## Showcase

Yellow this PR, Red main

```
cargo run --example many_buttons --release --features trace_tracy
```

`extract_uinode_background_colors`
<img width="448" alt="extract_uinode_background_colors"
src="https://github.com/user-attachments/assets/09c0f434-ab4f-4c0f-956a-cf31e9060061"
/>

`extract_uinode_images`
<img width="587" alt="extract_uinode_images"
src="https://github.com/user-attachments/assets/43246d7f-d22c-46d0-9a07-7e13d5379f56"
/>

`prepare_uinodes`
<img width="441" alt="prepare_uinodes_vec"
src="https://github.com/user-attachments/assets/cc9a7eac-60e9-42fa-8093-bce833a1c153"
/>
  • Loading branch information
ickshonpe authored Jan 30, 2025
1 parent 59697f9 commit ba1b009
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 207 deletions.
50 changes: 25 additions & 25 deletions crates/bevy_ui/src/render/box_shadow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ use bevy_color::{Alpha, ColorToComponents, LinearRgba};
use bevy_ecs::prelude::*;
use bevy_ecs::{
prelude::Component,
storage::SparseSet,
system::{
lifetimeless::{Read, SRes},
*,
Expand Down Expand Up @@ -225,12 +224,13 @@ pub struct ExtractedBoxShadow {
pub blur_radius: f32,
pub size: Vec2,
pub main_entity: MainEntity,
pub render_entity: Entity,
}

/// List of extracted shadows to be sorted and queued for rendering
#[derive(Resource, Default)]
pub struct ExtractedBoxShadows {
pub box_shadows: SparseSet<Entity, ExtractedBoxShadow>,
pub box_shadows: Vec<ExtractedBoxShadow>,
}

pub fn extract_shadows(
Expand Down Expand Up @@ -311,22 +311,19 @@ pub fn extract_shadows(
bottom_right: uinode.border_radius.bottom_right * spread_ratio,
};

extracted_box_shadows.box_shadows.insert(
commands.spawn(TemporaryRenderEntity).id(),
ExtractedBoxShadow {
stack_index: uinode.stack_index,
transform: transform.compute_matrix()
* Mat4::from_translation(offset.extend(0.)),
color: drop_shadow.color.into(),
bounds: shadow_size + 6. * blur_radius,
clip: clip.map(|clip| clip.clip),
extracted_camera_entity,
radius,
blur_radius,
size: shadow_size,
main_entity: entity.into(),
},
);
extracted_box_shadows.box_shadows.push(ExtractedBoxShadow {
render_entity: commands.spawn(TemporaryRenderEntity).id(),
stack_index: uinode.stack_index,
transform: transform.compute_matrix() * Mat4::from_translation(offset.extend(0.)),
color: drop_shadow.color.into(),
bounds: shadow_size + 6. * blur_radius,
clip: clip.map(|clip| clip.clip),
extracted_camera_entity,
radius,
blur_radius,
size: shadow_size,
main_entity: entity.into(),
});
}
}
}
Expand All @@ -346,7 +343,8 @@ pub fn queue_shadows(
draw_functions: Res<DrawFunctions<TransparentUi>>,
) {
let draw_function = draw_functions.read().id::<DrawBoxShadows>();
for (entity, extracted_shadow) in extracted_box_shadows.box_shadows.iter() {
for (index, extracted_shadow) in extracted_box_shadows.box_shadows.iter().enumerate() {
let entity = extracted_shadow.render_entity;
let Ok((default_camera_view, shadow_samples)) =
render_views.get_mut(extracted_shadow.extracted_camera_entity)
else {
Expand Down Expand Up @@ -374,13 +372,14 @@ pub fn queue_shadows(
transparent_phase.add(TransparentUi {
draw_function,
pipeline,
entity: (*entity, extracted_shadow.main_entity),
entity: (entity, extracted_shadow.main_entity),
sort_key: (
FloatOrd(extracted_shadow.stack_index as f32 + stack_z_offsets::BOX_SHADOW),
entity.index(),
),
batch_range: 0..0,
extra_index: PhaseItemExtraIndex::None,
index,
indexed: true,
});
}
Expand Down Expand Up @@ -417,11 +416,13 @@ pub fn prepare_shadows(
let mut indices_index = 0;

for ui_phase in phases.values_mut() {
let mut item_index = 0;

while item_index < ui_phase.items.len() {
for item_index in 0..ui_phase.items.len() {
let item = &mut ui_phase.items[item_index];
if let Some(box_shadow) = extracted_shadows.box_shadows.get(item.entity()) {
if let Some(box_shadow) = extracted_shadows
.box_shadows
.get(item.index)
.filter(|n| item.entity() == n.render_entity)
{
let rect_size = box_shadow.bounds.extend(1.0);

// Specify the corners of the node
Expand Down Expand Up @@ -532,7 +533,6 @@ pub fn prepare_shadows(
*ui_phase.items[item_index].batch_range_mut() =
item_index as u32..item_index as u32 + 1;
}
item_index += 1;
}
}
ui_meta.vertices.write_buffer(&render_device, &render_queue);
Expand Down
52 changes: 24 additions & 28 deletions crates/bevy_ui/src/render/debug_overlay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,34 +85,30 @@ pub fn extract_debug_overlay(
};

// Extract a border box to display an outline for every UI Node in the layout
extracted_uinodes.uinodes.insert(
commands.spawn(TemporaryRenderEntity).id(),
ExtractedUiNode {
// Add a large number to the UI node's stack index so that the overlay is always drawn on top
stack_index: uinode.stack_index + u32::MAX / 2,
color: Hsla::sequential_dispersed(entity.index()).into(),
rect: Rect {
min: Vec2::ZERO,
max: uinode.size,
},
clip: maybe_clip
.filter(|_| !debug_options.show_clipped)
.map(|clip| clip.clip),
image: AssetId::default(),
extracted_camera_entity,
item: ExtractedUiItem::Node {
atlas_scaling: None,
transform: transform.compute_matrix(),
flip_x: false,
flip_y: false,
border: BorderRect::all(
debug_options.line_width / uinode.inverse_scale_factor(),
),
border_radius: uinode.border_radius(),
node_type: NodeType::Border,
},
main_entity: entity.into(),
extracted_uinodes.uinodes.push(ExtractedUiNode {
render_entity: commands.spawn(TemporaryRenderEntity).id(),
// Add a large number to the UI node's stack index so that the overlay is always drawn on top
stack_index: uinode.stack_index + u32::MAX / 2,
color: Hsla::sequential_dispersed(entity.index()).into(),
rect: Rect {
min: Vec2::ZERO,
max: uinode.size,
},
);
clip: maybe_clip
.filter(|_| !debug_options.show_clipped)
.map(|clip| clip.clip),
image: AssetId::default(),
extracted_camera_entity,
item: ExtractedUiItem::Node {
atlas_scaling: None,
transform: transform.compute_matrix(),
flip_x: false,
flip_y: false,
border: BorderRect::all(debug_options.line_width / uinode.inverse_scale_factor()),
border_radius: uinode.border_radius(),
node_type: NodeType::Border,
},
main_entity: entity.into(),
});
}
}
Loading

0 comments on commit ba1b009

Please sign in to comment.