bevy-ui: Fix crash when despawning a Node that had its ChildOf removed. - #25386
bevy-ui: Fix crash when despawning a Node that had its ChildOf removed.#25386CyberspaceDreamn wants to merge 5 commits into
bevy-ui: Fix crash when despawning a Node that had its ChildOf removed.#25386Conversation
…its `ChildOf` component removed.
|
Welcome, new contributor! Please make sure you've read our contributing guide, as well as our policy regarding AI usage, and we look forward to reviewing your pull request shortly ✨ |
| // Remove parents of root nodes to avoid a potential panic (invalid SlotMap key used). | ||
| for root in removed_parents.read() { | ||
| if !node_query.contains(root) { | ||
| continue; | ||
| } | ||
|
|
||
| let Some(taffy_node) = ui_surface.entity_to_taffy.get(&root) else { | ||
| continue; | ||
| }; | ||
|
|
||
| let taffy_node = taffy_node.id; | ||
|
|
||
| let Some(taffy_parent) = ui_surface.taffy.parent(taffy_node) else { | ||
| continue; | ||
| }; | ||
|
|
||
| let _ = ui_surface.taffy.remove_child(taffy_parent, taffy_node); | ||
| } | ||
|
|
There was a problem hiding this comment.
This removes the new root from the previous parent's children, before the implicit viewport node is added. So then in update_children_recursively set_children for the previous parent doesn't see the root and doesn't overwrite the root's implicit viewport node parent. I think this works, but there mght be a shorter fix.
There was a problem hiding this comment.
The underlying problem is that the new root is given an implicit taffy parent representing the viewport in get_or_insert_taffy_viewport, but its not removed from its old parent's children at the same time. Then when the old parent's children are updated it sees the new root was removed and clears the new root's parent, which it expects to be the old parent, but it isn't, it's the new implicit viewport parent.
So it should be sufficient to just remove the new root node from its previous parent's (if it had a previous parent) taffy children in get_or_insert_taffy_viewport. If that makes sense.
…r `ChildOf` removed.
# Objective Fixes bevyengine#25378 ## Solution As I understand, Bevy ECS uses local thread occupancy marker for systems that must run on the local thread. This would be exclusive systems (no other system runs) or non-send systems (can not move off the ECS "main" thread). The executor marked the local thread as free after non-send systems, but not after send exclusive systems. This fix clears the `local_thread_running` flag now after exclusive system has ran, in addition to the previous non-send clearance. ## Testing - The PR contains a regression test using exclusive-system followed by non-send system. - Verified that both tests fail without the fix. - Ran the full bevy ECS test suite with the fix, passes. AI disclosure: the bug debugging & fix suggestion + code suggestion for #[test]'s was done by AI.
|
You are mostly correct, but it is the root being present in multiple from if let Some(children) = self.children.get(key) {
for child in children.iter().copied() {
self.parents[child.into()] = None; // crashes here with `invalid SlotMap key used`
}
}If I believe the root cause is an unresolved bug in Either way, explicitly removing its previous parent in |
|
This seems to work reliably as a regression test: #[test]
fn node_with_sibling_should_have_implicit_viewport_node_parent_after_unparenting() {
#[derive(Component)]
struct Marker;
let mut app = setup_ui_test_app();
let world = app.world_mut();
world.spawn(Node::default());
let child_a = world.spawn(Node::default()).id();
let child_b = world.spawn(Node::default()).id();
world
.spawn((Node::default(), Marker))
.add_children(&[child_a, child_b]);
app.update();
app.world_mut().entity_mut(child_a).remove::<ChildOf>();
app.update();
let ui_surface = app.world().resource::<UiSurface>();
let node = ui_surface.entity_to_taffy[&child_a];
assert_eq!(ui_surface.taffy.parent(node.id), node.viewport_id);
assert!(ui_surface
.taffy
.children(node.viewport_id.unwrap())
.is_ok_and(|children| children.contains(&node.id)));
}It makes assumptions about archetypes and query ordering etc that might not hold in later bevy versions though. |
|
it looks right to me but I'm not experienced enough with taffy to say for sure. |
Objective
Prevent an occasional crash in
taffy/bevy_uiwhen despawningNodes that had theirChildOfcomponent removed.Fixes #25303. 1
Solution
When a
Nodehas itsChildOfremoved, also remove itsTaffyTree::parent. I believe this preventsbevyfrom callingTaffyTree::removetwice with the sameNodeId.Testing
bevy_uiunit tests 2Footnotes
I cannot replicate the crash with this patch applied, but solid proof is difficult with heisenbugs. ↩
Tested only on Linux, but as this is a logic bug platform should not matter. ↩ ↩2