Skip to content

Store taffy node keys in components #8297

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

Open
wants to merge 71 commits into
base: main
Choose a base branch
from

Conversation

ickshonpe
Copy link
Contributor

@ickshonpe ickshonpe commented Apr 3, 2023

Objective

Store the taffy node keys in a component on the UI node entities instead of using the UiSurface::entity_to_taffy map to look them up.

Changelog

UiKey:

  • UiKey is a new component with a single field: taffy_id: taffy::node::Node and a method get to retrieve its value.
  • UiKey::default() creates a UiKey with a null taffy id value. This should be the only way for users to instantiate a UiNodeId component. If multiple entities with the same non-null UiKey::taffy_id exist, the UI layout will not update correctly.
  • All UI nodes must have a UiKey component to be included in the UI layout.

NodeBundle, ImageBundle, TextBundle, ButtonBundle:

  • The UiKey component has been added to each of these bundles.

UiSurface:

  • The upsert_node method has been split into separate methods: insert_node and set_style.
  • Removed get_layout. The Taffy layout is accessed directly instead.
  • Most methods now accept a taffy::node::Node parameter instead since entity_to_taffy lookups are no longer needed.

LayoutError

  • Removed as no longer used, neither error should ever occur.

ui_layout_system

  • Removal detection is performed first before updates. Otherwise, the UI node entity tree and the Taffy layout tree could get out of sync if a user removes and then, later in the same frame, reinserts a component used by the UI.

Migration Guide

UiKey is a new component that is managed automatically by ui_layout_system and should only be instantiated with UiKey::default(). It is used to identify a UI node in the UI's internal layout tree.

UiKey has been added to each of the UI node bundles.

* Added new components "TaffyNode" and "TaffyParent"
* Added `insert_node` method to `FlexSurface`
* Added `make_measure` function to `flex` module.
* `flex_node_system` stores taffy nodes keys in the "TaffyNode" and "TaffyParent" components instead of looking them up using the `entity_to_taffy` map.
@ickshonpe ickshonpe changed the title Store taffy node keys in the UI entities instead of looking them up Store taffy node keys in UI node entities instead of looking them up Apr 3, 2023
@ickshonpe
Copy link
Contributor Author

cargo run --profile stress-test --example many_buttons --features trace_tracy -- no-text

flex_node_system_tracy

@ickshonpe ickshonpe changed the title Store taffy node keys in UI node entities instead of looking them up Store taffy node keys in components Apr 3, 2023
@alice-i-cecile alice-i-cecile added C-Performance A change motivated by improving speed, memory usage or compile times A-UI Graphical user interfaces, styles, layouts, and widgets labels Apr 3, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Apr 3, 2023

You added a new example but didn't add metadata for it. Please update the root Cargo.toml file.

@alice-i-cecile alice-i-cecile added this to the 0.11 milestone Apr 3, 2023
* added `primary_node` and `physical_to_logical_factor` fields to `FlexSurface`.
* added the `Transforms` variant to the `UiSystem` enum.
* split off the transforms update from `flex_node_system` into a separate function `update_ui_node_transform`
and scheduled it to run after `UiSystem::Flex` and before `TransformSystem::TransformPropagate`.
* removed `TaffyParent` component, added `parent` field to `TaffyNode` component
* removed `PrimaryNode` field from `FlexSurface`
@mockersf
Copy link
Member

mockersf commented Apr 4, 2023

Is it possible to remove the entity_to_taffy hashmap? Otherwise it would mean storing the same information twice

@ickshonpe
Copy link
Contributor Author

ickshonpe commented Apr 4, 2023

Is it possible to remove the entity_to_taffy hashmap? Otherwise it would mean storing the same information twice

I don't think so, it's needed for handling children and removing orphaned taffy nodes.
Maybe if Taffy could be made to store the layout data directly in the Bevy UI entities instead of using SlotMap.

@nicoburns
Copy link
Contributor

Is it possible to remove the entity_to_taffy hashmap? Otherwise it would mean storing the same information twice

I don't think so, it's needed for handling children and removing orphaned taffy nodes. Maybe if Taffy could be made to store the layout data directly in the Bevy UI entities instead of using SlotMap.

DioxusLabs/taffy#326 (or more likely, parts of this PR split out into their own PRs) will allow this. It's supposed to be possible with the current version of the LayoutTree trait, but in practice the current version has things like SlotMap keys in the trait function signatures which make it impractical.

With that approach all Taffy data (input styles, parent-children relationships, and and output layouts) would be stored by Bevy (however it chose to do so). Care would need to be taken to ensure performance parity though, as Taffy's SlotMaps are backed by Vecs and are therefore very fast to access. Not sure if it's possible to get Vec backed storage in bevy_ecs that only applies to UI nodes?

@nicoburns
Copy link
Contributor

nicoburns commented Apr 4, 2023

Although to this specific issue there might be a simpler solution. My understanding is that a Bevy component typically is a HashMap, so a Query of a component containing the Taffy node key ought to function the same as a HashMap containing the mapping I think.

* Renamed `Node` to `NodeSize`
* Renamed `TaffyNode` to `Node` and removed its `Parent` field.
* Split off parts of `flex_node_system` into separate systems:
`synchronise_children`, `insert_new_ui_nodes_into_layout` and `clean_up_removed_ui_nodes`.
@github-actions
Copy link
Contributor

Example alien_cake_addict failed to run, please try running it locally and check the result.

1 similar comment
@github-actions
Copy link
Contributor

Example alien_cake_addict failed to run, please try running it locally and check the result.

* Renamed `flex_node_system` to `update_ui_layout`.
* Replaced the `window_nodes` map field of  `UiSurface` with a single `window_node` node field.
* Removed redundant `Node` component removal.
* Query for `Node` components of root nodes instead of `Entity` so that `set_window_children` doesn't have to look up the keys.
* Added a a simple test for node insertion and removal.
…ield and `get` method.

Edited the doc comment for `insert_node`.
…e `layout` module.

Renamed some variables to be consistant with the name change.

`UiSurface::insert_node` reuses the previous taffy node associated to the given `entity`, if one exists.
@ickshonpe
Copy link
Contributor Author

Improved the implementation and reduced it down to just the minimum necessary changes without any extra systems. Should be ready to merge, but I'll make some new tests for UiSurface.

Changed the ordering of parameters for the `from_style` and `LayoutContext::new` functions to put the most important parameter first.

`UiSurface`:
Added a test `uisurface_insert_and_remove_nodes`.
Type alias for `taffy::node::Node`.

`UiSurface::get_key`
Method that can be used to lookup taffy nodes from `entity_to_taffy` but takes and returns a value rather than a borrow.
Type alias for `taffy::node::Node`.

`UiSurface::get_key`
Method that can be used to lookup taffy nodes from `entity_to_taffy` but takes and returns a value rather than a borrow.
@nicoburns
Copy link
Contributor

@ickshonpe What's the motivation for this change? Performance? If so it would be good to get some perf numbers vs. main for the latest version.

@mockersf
Copy link
Member

mockersf commented Jun 19, 2023

It's increasing performances at the cost of memory. Not sure it's worth it in this case, as the gain is 0.2ms in an extreme UI case...

@alice-i-cecile alice-i-cecile added the X-Controversial There is active debate or serious implications around merging this PR label Jun 19, 2023
@cart
Copy link
Member

cart commented Jul 8, 2023

I do like the idea of breaking this out from a centralized hashmap (and perf wins are good), but having multiple sources of truth that need syncing gives me some pause. I'll need to give this some thought, which sadly will need to wait until 0.12.

@cart cart modified the milestones: 0.11, 0.12 Jul 8, 2023
@alice-i-cecile alice-i-cecile removed this from the 0.12 milestone Sep 29, 2023
@BenjaminBrienen BenjaminBrienen added D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Jan 23, 2025
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-Performance A change motivated by improving speed, memory usage or compile times D-Modest A "normal" level of difficulty; suitable for simple features or challenging fixes S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged X-Controversial There is active debate or serious implications around merging this PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants