Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplified ui_stack_system #9889

Merged
merged 43 commits into from
Sep 30, 2024

Conversation

ickshonpe
Copy link
Contributor

@ickshonpe ickshonpe commented Sep 21, 2023

Objective

ui_stack_system generates a tree of StackingContexts which it then flattens to get the UiStack.

But there's no need to construct a new tree. We can query for nodes with a global ZIndex, add those nodes to the root nodes list and then build the UiStack from a walk of the existing layout tree, ignoring any branches that have a global Zindex.

Fixes #9877

Solution

Split the ZIndex enum into two separate components, ZIndex and GlobalZIndex

Query for nodes with a GlobalZIndex, add those nodes to the root nodes list and then build the UiStack from a walk of the existing layout tree, filtering branches by Without<GlobalZIndex> so we don't revisit nodes.

cargo run --profile stress-test --features trace_tracy --example many_buttons
ui-stack-system-walk-split-enum

(Yellow is this PR, red is main)


Changelog

Zindex

  • The ZIndex enum has been split into two separate components ZIndex (which replaces ZIndex::Local) and GlobalZIndex (which replaces ZIndex::Global). An entity can have both a ZIndex and GlobalZIndex, in comparisons ZIndex breaks ties if two GlobalZIndex values are equal.

ui_stack_system

  • Instead of generating a tree of StackingContexts, query for nodes with a GlobalZIndex, add those nodes to the root nodes list and then build the UiStack from a walk of the existing layout tree, filtering branches by Without<GlobalZIndex so we don't revisit nodes.

Migration Guide

The ZIndex enum has been split into two separate components ZIndex (which replaces ZIndex::Local) and GlobalZIndex (which replaces ZIndex::Global). An entity can have both a ZIndex and GlobalZIndex, in comparisons ZIndex breaks ties if two GlobalZindex values are equal.

@ickshonpe ickshonpe mentioned this pull request Sep 21, 2023
@ickshonpe ickshonpe mentioned this pull request Sep 21, 2023
@oceantume
Copy link
Contributor

oceantume commented Sep 21, 2023

That makes sense to me. While both are kinds of zindex, they have different implications, both conceptually and in the implementation.

I'm curious to know what maintainers think of the naming, i.e. should ZIndex be renamed to LocalZIndex so that it's in direct contrast with GlobalZIndex?

Copy link
Contributor

@oceantume oceantume left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few minor missing changes I commented about, but otherwise this looks good to me. This makes ZIndex a simpler component and "hides" global z-index behavior which is probably a good thing since it's an escape hatch and isn't meant to be used in most UI's. Unlike the previous PR, this is not a feature regression so it's less controversial in my opinion (as long as the API change is deemed uncontroversial as well).

crates/bevy_ui/src/ui_node.rs Outdated Show resolved Hide resolved
crates/bevy_ui/src/stack.rs Show resolved Hide resolved
crates/bevy_ui/src/ui_node.rs Outdated Show resolved Hide resolved
crates/bevy_ui/src/stack.rs Show resolved Hide resolved
examples/ui/z_index.rs Outdated Show resolved Hide resolved
ickshonpe and others added 2 commits September 22, 2023 19:06
Co-authored-by: Gabriel Bourgeois <gabriel.bourgeoisv4si@gmail.com>
Co-authored-by: Gabriel Bourgeois <gabriel.bourgeoisv4si@gmail.com>
@ickshonpe
Copy link
Contributor Author

ickshonpe commented Sep 23, 2023

That makes sense to me. While both are kinds of zindex, they have different implications, both conceptually and in the implementation.

I'm curious to know what maintainers think of the naming, i.e. should ZIndex be renamed to LocalZIndex so that it's in direct contrast with GlobalZIndex?

My thinking is that keeping ZIndex for the local ZIndex is lower friction. It's shorter, many users will already be familiar with the equivalent-ish CSS z-index property and those that aren't will get the z-index entry on mdn etc if they search for it.

I'm not bothered about the naming though, if there is a consensus in favour of LocalZIndex it would be fine.

Copy link
Contributor

@atlv24 atlv24 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes a lot of sense and uses the ECS better. Im in favor of the current naming (ZIndex + GlobalZIndex)

@ickshonpe ickshonpe changed the title simplified ui_stack_system Simplified ui_stack_system Sep 24, 2023
@nicoburns nicoburns added A-UI Graphical user interfaces, styles, layouts, and widgets C-Code-Quality A section of code that is hard to understand or change C-Usability A targeted quality-of-life change that makes Bevy easier to use labels Sep 26, 2023
crates/bevy_ui/src/stack.rs Outdated Show resolved Hide resolved
crates/bevy_ui/src/stack.rs Show resolved Hide resolved
crates/bevy_ui/src/stack.rs Outdated Show resolved Hide resolved
@alice-i-cecile alice-i-cecile added this to the 0.15 milestone Sep 21, 2024
@alice-i-cecile alice-i-cecile added S-Needs-Benchmarking This set of changes needs performance benchmarking to double-check that they help and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Sep 21, 2024
@villor villor mentioned this pull request Sep 22, 2024
21 tasks
crates/bevy_ui/src/stack.rs Outdated Show resolved Hide resolved
crates/bevy_ui/src/stack.rs Outdated Show resolved Hide resolved
.ok()
.map(|zindex| (*entity, zindex.map(|zindex| zindex.0).unwrap_or(0)))
})
.collect();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to collect, use a shared vec sourced from a local and clear -> extend here.

.map(|zindex| (*entity, zindex.map(|zindex| zindex.0).unwrap_or(0)))
})
.collect();
radsort::sort_by_key(&mut z_children, |k| k.1);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not clear that radsort is better than std::sort here either, since most nodes will have only a handful of children. To use std::sort_unstable you can add an index for each child and sort by (z_index, child_index).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another reviewer said to try using radsort and it seemed to be an improvement with my benchmarks but I forgot that those benchmarks were with very large numbers of children which isn't the typical usage case.

Comment on lines 86 to 90
for (i, entity) in ui_stack.uinodes.iter().enumerate() {
if let Ok(mut node) = update_query.get_mut(*entity) {
node.bypass_change_detection().stack_index = i as u32;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The traversal_stack is never inserted to ui_stack. Am I missing something here? Also how are children supposed to be inserted relative to their parents within the global stack?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm puzzled how this passes CI, it appears to have serious bugs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

traversal_stack is used to navigate the tree, not insert the nodes.

Copy link
Contributor Author

@ickshonpe ickshonpe Sep 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Abandoned the iterative version using stack traversal anyway, and went back to walking the tree recursively, copying your stack of cached buffers approach from main to avoid the vec allocations. I don't understand why the iterative version is so much less efficient though. It just has one buffer for storing and sorting the children it reuses, which seems so much better.

@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Benchmarking This set of changes needs performance benchmarking to double-check that they help labels Sep 27, 2024
@alice-i-cecile alice-i-cecile removed this from the 0.15 milestone Sep 27, 2024
@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Sep 30, 2024
Copy link
Contributor

@UkoeHB UkoeHB left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks correct now, minor doc comments. Also would like to see an updated perf graph.

crates/bevy_ui/src/stack.rs Show resolved Hide resolved
crates/bevy_ui/src/ui_node.rs Outdated Show resolved Hide resolved
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
@alice-i-cecile alice-i-cecile added this pull request to the merge queue Sep 30, 2024
Merged via the queue into bevyengine:main with commit c5742ff Sep 30, 2024
26 checks passed
robtfm pushed a commit to robtfm/bevy that referenced this pull request Oct 4, 2024
# Objective

`ui_stack_system` generates a tree of `StackingContexts` which it then
flattens to get the `UiStack`.

But there's no need to construct a new tree. We can query for nodes with
a global `ZIndex`, add those nodes to the root nodes list and then build
the `UiStack` from a walk of the existing layout tree, ignoring any
branches that have a global `Zindex`.

Fixes bevyengine#9877

## Solution

Split the `ZIndex` enum into two separate components, `ZIndex` and
`GlobalZIndex`

Query for nodes with a `GlobalZIndex`, add those nodes to the root nodes
list and then build the `UiStack` from a walk of the existing layout
tree, filtering branches by `Without<GlobalZIndex>` so we don't revisit
nodes.

```
cargo run --profile stress-test --features trace_tracy --example many_buttons
```

<img width="672" alt="ui-stack-system-walk-split-enum"
src="https://github.com/bevyengine/bevy/assets/27962798/11e357a5-477f-4804-8ada-c4527c009421">

(Yellow is this PR, red is main)

---

## Changelog
`Zindex`
* The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZIndex` values are equal.

`ui_stack_system`
* Instead of generating a tree of `StackingContexts`, query for nodes
with a `GlobalZIndex`, add those nodes to the root nodes list and then
build the `UiStack` from a walk of the existing layout tree, filtering
branches by `Without<GlobalZIndex` so we don't revisit nodes.

## Migration Guide

The `ZIndex` enum has been split into two separate components `ZIndex`
(which replaces `ZIndex::Local`) and `GlobalZIndex` (which replaces
`ZIndex::Global`). An entity can have both a `ZIndex` and
`GlobalZIndex`, in comparisons `ZIndex` breaks ties if two
`GlobalZindex` values are equal.

---------

Co-authored-by: Gabriel Bourgeois <gabriel.bourgeoisv4si@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: UkoeHB <37489173+UkoeHB@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-UI Graphical user interfaces, styles, layouts, and widgets C-Code-Quality A section of code that is hard to understand or change C-Performance A change motivated by improving speed, memory usage or compile times C-Usability A targeted quality-of-life change that makes Bevy easier to use S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants