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

Child text components created after initial object creation are invisible #18616

Closed
jansenMurphy opened this issue Mar 30, 2025 · 6 comments · Fixed by #18664
Closed

Child text components created after initial object creation are invisible #18616

jansenMurphy opened this issue Mar 30, 2025 · 6 comments · Fixed by #18664
Labels
A-Rendering Drawing game state to the screen A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior S-Ready-For-Implementation This issue is ready for an implementation PR. Go for it!
Milestone

Comments

@jansenMurphy
Copy link

jansenMurphy commented Mar 30, 2025

Bevy version

0.16.0-rc.2

Relevant system information

Rust version: 1.87.0-nightly (6cf826701 2025-03-14)
AdapterInfo

  • name: "NVIDIA GeForce RTX 3060",
  • vendor: 4318, device: 9476
  • device_type: DiscreteGpu
  • driver: "NVIDIA"
  • driver_info: "560.94"
  • backend: Vulkan

Windows 11 (build 22631) - Multi-window, 2 monitors - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 3060 (NVIDIA; 32.0.15.6094) - AMD Ryzen 9 5900X 12-Core Processor (24 threads)

What you did

https://github.com/jansenMurphy/bevy_minimum_text_render_failure_example

With this example code, click 'Menu A' and 'Menu B' repeatedly to add and remove the entities and see how the text in menu B fails to render as expected.

This was as minimal as I knew how to get it:


#[derive(States, Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash, Default)]
enum AppState{
    #[default]
    StateA,
    StateB,
}

#[derive(Component)]
struct MenuRoot;
#[derive(Component)]
struct MenuStateARoot;
#[derive(Component)]
struct MenuStateBRoot;

fn main() {

    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(OnEnter(AppState::StateA), setup_a)
        .add_systems(OnExit(AppState::StateA), teardown::<MenuStateARoot>)
        .add_systems(OnEnter(AppState::StateB), setup_b)
        .add_systems(OnExit(AppState::StateB), teardown::<MenuStateBRoot>)
        .add_systems(Startup, setup_root)
        .init_state::<AppState>()
        .run();
}

fn setup_root(mut commands: Commands){

    commands.spawn((
        Camera {
            order: 20,
            ..Default::default()
        },
        Camera2d,
        IsDefaultUiCamera,
    ));

    commands.spawn((
        MenuRoot,
        Node{
            ..Default::default()
        },
        Button
    )).with_children(|parent|{
        parent.spawn((
            Button,
            Text::new("Menu A")
        )).observe(|
            mut _trigger: Trigger<Pointer<Click>>,
            mut next_app_state: ResMut<NextState<AppState>>
        | {
            println!("To A");
            next_app_state.set(AppState::StateA);
        });

        parent.spawn((
            Button,
            Text::new("Menu B")
        )).observe(|
            mut _trigger: Trigger<Pointer<Click>>,
            mut next_app_state: ResMut<NextState<AppState>>
        | {
            println!("To B");
            next_app_state.set(AppState::StateB);
        });
    });
}

fn setup_a(
    mut commands: Commands,
    q: Query<Entity, With<MenuRoot>>,
){
    if let Ok(parent) = q.single(){

        println!("Setup A");
        commands.spawn((
            ChildOf{parent},
            MenuStateARoot,
            Text::new("This text appears"),
            BackgroundColor(Color::srgb(0.0, 1.0, 0.0))
        ));

        commands.spawn((
            ChildOf{parent},
            Button,
            BackgroundColor(Color::srgb(0.5, 0.5, 0.0))
        ));
    }
}

fn setup_b(
    mut commands: Commands,
    q: Query<Entity, With<MenuRoot>>,
){
    if let Ok(parent) = q.single(){

        println!("Setup B");
        commands.spawn((
            ChildOf{parent},
            MenuStateBRoot,
            Node{..Default::default()},
            BackgroundColor(Color::srgb(0.1, 0.1, 0.1))
        )).with_children(|sub_parent|{
            sub_parent.spawn((
                Text::new("This text should appear but does not"),
                BackgroundColor(Color::srgb(0.0, 0.0, 1.0)),
            ));
        });

        commands.spawn((
            ChildOf{parent},
            Button,
            BackgroundColor(Color::srgb(0.0, 0.5, 0.5))
        ));
    }
}

fn teardown<T: Component>(to_despawn: Query<Entity, With<T>>, mut commands: Commands){
    for entity in &to_despawn{
        commands.entity(entity).despawn();
    }
}

What went wrong

I think the problem is that when entities with text components are added as children to already-existing ui entities, the text is laid out properly but not rendered.

I was expecting to be able to add child 'menus' with text to a root menu long after instantiating the root of the menu.

Additional information

The dark grey rectangle should be rendering text but is not. It's the appropriate width for the text content
Image

Apologies if this is already an issue; I didn't see it if it was.

@jansenMurphy jansenMurphy added C-Bug An unexpected or incorrect behavior S-Needs-Triage This issue needs to be labelled labels Mar 30, 2025
@alice-i-cecile alice-i-cecile added A-UI Graphical user interfaces, styles, layouts, and widgets S-Needs-Investigation This issue requires detective work to figure out what's going wrong A-Text Rendering and layout for characters and removed S-Needs-Triage This issue needs to be labelled labels Mar 31, 2025
@alice-i-cecile
Copy link
Member

Did this work in Bevy 0.15? Trying to evaluate for the milestone + figure out the root cause.

@alice-i-cecile alice-i-cecile added this to the 0.16 milestone Mar 31, 2025
@jansenMurphy
Copy link
Author

It looks like it's a 0.16 thing. I made a minimum version in 0,15 with some required API changes and then another in 0.16 that is as close to the match of the 0.15 version as I could. The 0.15 version worked as anticipated, but both 0.16 branches I made have the same undesired behavior.

https://github.com/jansenMurphy/bevy_minimum_text_render_failure_example/tree/bevy-015-version
https://github.com/jansenMurphy/bevy_minimum_text_render_failure_example/tree/bevy-016-version-2

@alice-i-cecile alice-i-cecile added the A-ECS Entities, components, systems, and events label Mar 31, 2025
@polygon
Copy link
Contributor

polygon commented Mar 31, 2025

Did a bisect:

300fe4db4d58b1a0a0e74fb9026ab494c2b88e80 is the first bad commit

Looks like this got broken in this commit

@alice-i-cecile
Copy link
Member

#17579 by @ickshonpe :) Thanks a ton for bisecting: that will speed things up substantially.

@alice-i-cecile alice-i-cecile added A-Rendering Drawing game state to the screen and removed A-ECS Entities, components, systems, and events A-Text Rendering and layout for characters labels Mar 31, 2025
@ickshonpe
Copy link
Contributor

Ah it didn't occur to me there could be a problem with text components, I'll take a look

@ickshonpe
Copy link
Contributor

ickshonpe commented Apr 1, 2025

Right, got it. If both a child and prent are added at the same time and update_ui_context_system's reparent_nodes_query visits the child before its parent, the child's ComputedNodeTarget is left unset as it's updated from the parent's target before the parent's target is set.

The problem is the visited HashSet that tracks which entities update_contexts_recursively has visited but it only works when the reparent_nodes_query is ordered descending from parent to child and is redundant anyway the set_if_neq check is sufficient to track already updated nodes.

@ickshonpe ickshonpe added S-Ready-For-Implementation This issue is ready for an implementation PR. Go for it! and removed S-Needs-Investigation This issue requires detective work to figure out what's going wrong S-Ready-For-Implementation This issue is ready for an implementation PR. Go for it! labels Apr 1, 2025
github-merge-queue bot pushed a commit that referenced this issue Apr 1, 2025
…m`. (#18664)

# Objective

The `visited: Local<HashSet<Entity>>` system param is meant to track
which entities `update_contexts_recursively` has visited and updated but
when the reparent_nodes_query isn't ordered descending from parent to
child nodes can get marked as visited even though their camera target is
unset and if the camera target is unset then the node won't be rendered.

Fixes #18616

## Solution

Remove the `visited` system param from `update_ui_context_system` and
the associated visited check from `update_contexts_recursively`. It was
redundant anyway since the set_if_neq check is sufficient to track
already updated nodes.

## Testing

The example from #18616 can be used for testing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen A-UI Graphical user interfaces, styles, layouts, and widgets C-Bug An unexpected or incorrect behavior S-Ready-For-Implementation This issue is ready for an implementation PR. Go for it!
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants