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

Assign view id paths by parents #9

Closed
wants to merge 1 commit into from

Conversation

Zoxc
Copy link
Collaborator

@Zoxc Zoxc commented Nov 24, 2022

This changes Id allocation from using a global unique id per view to letting containers pick unique Id segments for their children. The IdPath when the build method on a view is called uniquely identifies an view. This results in slightly cleaner code. Views like VStack can process event dispatch with a jump table by hardcoding children ids instead of using an if/else tree.

A further optimization would be to change IdPath to Vec<u8> and let containers only use the number of bytes required, reducing IdPath sizes overall.

@raphlinus
Copy link
Contributor

I'm still wrapping my head around this one, and am not yet really seeing the advantage, but I want to fully understand it before making a decision.

One observation: a reason to make the id mutable in rebuild was to make sure a change of type in an AnyView got a fresh id, that seems to no longer be the case. Whether that seriously breaks anything I still haven't figured out.

In any case, thanks for these. I do want to explore the neighborhood of the original Xilem draft and iterate toward things that are actually improvements.

@Zoxc
Copy link
Collaborator Author

Zoxc commented Nov 24, 2022

AnyView must be changed to create a unique id for each view it builds. Just increasing a local counter is sufficient.

@dfrg
Copy link
Contributor

dfrg commented Nov 29, 2022

I appreciate some of the reduced boilerplate but this seems to trade a simple, common Id allocation strategy with one that places a burden on containers. For simple cases like fixed VStacks this seems nice, but those are also likely to be small and cheap to search for a specific child. In the case of larger, dynamic containers, it seems like adding or removing children would negate any advantage of linear local id allocation scheme. We'd need to resort to a hashmap or linear search anyway if we want to retain stability. But I may be missing something.

@infogulch
Copy link

It seems like the use cases for ids have goals that pull in conflicting directions: you want ids to be static for the life of each component, and also support dynamic allocation under heavy component construction-destruction load. Maybe something like a generational index 1 2 would be useful here? 3 4

Footnotes

  1. https://docs.rs/slotmap/1.0.6/slotmap/index.html

  2. https://docs.rs/bevy_ecs/0.9.0/bevy_ecs/entity/struct.Entity.html

  3. https://lucassardois.medium.com/generational-indices-guide-8e3c5f7fd594

  4. https://news.ycombinator.com/item?id=17977906

@Zoxc
Copy link
Collaborator Author

Zoxc commented Nov 29, 2022

@dfrg Dynamic containers could just use the existing strategy of a global Id allocation for its children, so I don't think an additional burden is required.

It's possible a generational index would be an optimization that dynamic containers could use for better event dispatch.

@raphlinus
Copy link
Contributor

I've got my head around this - thanks again for the proposal and discussion. It is a viable alternative way to organize id paths. However, I'm going to close it, as I think it's a sideways move.

Yes, the signatures of build and rebuild become smaller, and the dispatch of events by a tuple to a child is more direct, but I'm also seeing the downsides, which to me outweigh the benefits: async resolution is by the IdPath rather than Id (and I think this may be a hot path in some cases), and id paths can be twice as long in the AnyView case, which may be common when binding to a scripting language.

Just a suggestion, but a blog post on the design neighborhood of the Xilem architecture could make a good read - this proposal as well as the one that's generic over widget trait so can support both native and web from the same app logic.

@raphlinus raphlinus closed this Nov 29, 2022
@Zoxc
Copy link
Collaborator Author

Zoxc commented Nov 29, 2022

async resolution is by the IdPath rather than Id (and I think this may be a hot path in some cases)

A major motivation is to optimize event dispatch, which is the mechanism used by async resolution. It relies on IdPath before and after this change, so I don't follow your logic here. The Id to IdPath change in this PR is just for the outstanding async counter, which should get a more optimized representation anyway.

id paths can be twice as long in the AnyView case, which may be common when binding to a scripting language.

I'd expect IdPath to be shorter overall if we make it use bytes and let containers represent things more efficiently. Also note that simple view like Button would not be longer when stored in a AnyView since the Id is just shifted to the parent and the button itself no longer needs an Id. AnyView can be optimized to only add a single byte to the path for the initial view mounted too.

github-merge-queue bot pushed a commit that referenced this pull request Apr 26, 2024
Alternative to #218

The version of viewsequence added in #205 is robust for all properties
supported in that version. However, it would be incompatible with future
expansions to the Xilem model, in particular async wiring.

In this PR, I propose a system to resolve this, which uses generational
indexes in the view id path (for the items where this is relevant).

Some other historical record: #9

This brings back the `View::State` associated type (renamed to
`ViewState` and `SeqState` depending on the trait), in order to perform
this wiring.
github-merge-queue bot pushed a commit that referenced this pull request Nov 11, 2024
New tracy image:


![image](https://github.com/user-attachments/assets/94e54c89-8159-4dd4-a521-4a7122f64375)

New log tracing file:
<details><summary>An overview of the new logs</summary>
<p>

```
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}: masonry::passes::update: RootWidget received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}:Prose{id=#1}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#3}:Label{id=#2}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#5}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#8}:SizedBox{id=#7}:Flex{id=#6}:Flex{id=#5}:VariableLabel{id=#4}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#10}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#10}:Label{id=#9}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#12}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#12}:Label{id=#11}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#14}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#14}:Label{id=#13}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#16}: masonry::passes::update: Button received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Flex{id=#17}:Button{id=#16}:Label{id=#15}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}: masonry::passes::update: Portal received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}: masonry::passes::update: SizedBox received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}:Prose{id=#18}: masonry::passes::update: Prose received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#20}:Label{id=#19}: masonry::passes::update: Label received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#22}: masonry::passes::update: Flex received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#24}:Flex{id=#23}:Flex{id=#22}:VariableLabel{id=#21}: masonry::passes::update: VariableLabel received Update::WidgetAdded
14:37:40.365Z TRACE update_new_widgets:RootWidget{id=#165}:Flex{id=#164}:Portal{id=#163}:Flex{id=#160}:SizedBox{id=#31}: masonry::passes::update: SizedBox received Update::WidgetAdded
```

</p>
</details> 

This was originally an experiment into caching spans, but I determined
that was non-viable due to the pass names.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants