update_text2d_layout creates new font atlases when the primary window is closed (#7849)

# Objective

Necessary conditions:
* Scale factor != 1
* Text is being displayed with Text2d
* The primary window is closed on a frame where the text or text's
bounds are modified.

Then when `update_text2d_layout` runs, it finds no primary window and
assumes a scale factor of 1.
The previous scale_factor was not equal to 1 and the text pipeline's old
font atlases were created for a non-1 scale factor, so it creates new
font atlases even though the app is closing.

The bug was first identified in #6666

## Minimal Example

```rust
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(WindowPlugin {
            primary_window: Some(Window {
                present_mode: bevy:🪟:PresentMode::Immediate,
                ..Default::default()
            }),
            ..default()
        }))
        .insert_resource(UiScale { scale: std::f64::consts::PI })
        .add_startup_system(setup)
        .add_system(update)
        .run();
}

fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
    commands.spawn(Camera2dBundle::default());
    commands.spawn(Text2dBundle {
        text: Text {
            sections: (0..10).map(|i| TextSection {
                value: i.to_string(),
                style: TextStyle {
                    font: asset_server.load("fonts/FiraSans-Bold.ttf"),
                    font_size: (10 + i) as f32,
                    color: Color::WHITE,
                }
            }).collect(),
            ..Default::default()            
        },
        ..Default::default()
    });
}

fn update(mut text: Query<&mut Text>) {
    for mut text in text.iter_mut() {
        text.set_changed();
    }
}
```
## Output
On closing the window you'll see the warning (if you don't, increase the
number of text sections):
```
WARN bevy_text::glyph_brush: warning[B0005]: Number of font atlases has exceeded the maximum of 16. Performance and memory usage may suffer.
```
The app should only create font atlases on startup, but it doesn't
display this warning until after you close the window

## Solution

Skip `update_text_layout` when there is no primary window.

## Changelog
* If no primary window is found, skip `update_text2d_layout`.
* Added a `Local` flag `skipped` to `update_text2d_layout`. This should
ensure there are no edge cases where text might not get drawn at all.

---------

Co-authored-by: François Mockers <mockersf@gmail.com>
This commit is contained in:
ickshonpe 2025-01-14 01:01:31 +00:00 committed by GitHub
parent 4dfa87798f
commit b90329aef5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -222,7 +222,7 @@ pub fn extract_text2d_sprite(
/// [`ResMut<Assets<Image>>`](Assets<Image>) -- This system only adds new [`Image`] assets.
/// It does not modify or observe existing ones.
pub fn update_text2d_layout(
mut last_scale_factor: Local<f32>,
mut last_scale_factor: Local<Option<f32>>,
// Text items which should be reprocessed again, generally when the font hasn't loaded yet.
mut queue: Local<EntityHashSet>,
mut textures: ResMut<Assets<Image>>,
@ -245,13 +245,15 @@ pub fn update_text2d_layout(
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
let scale_factor = windows
.get_single()
.ok()
.map(|window| window.resolution.scale_factor())
.unwrap_or(1.0);
.or(*last_scale_factor)
.unwrap_or(1.);
let inverse_scale_factor = scale_factor.recip();
let factor_changed = *last_scale_factor != scale_factor;
*last_scale_factor = scale_factor;
let factor_changed = *last_scale_factor != Some(scale_factor);
*last_scale_factor = Some(scale_factor);
for (entity, block, bounds, text_layout_info, mut computed) in &mut text_query {
if factor_changed