From b90329aef5ae48b25671e71e5b0a7a75b658ff93 Mon Sep 17 00:00:00 2001 From: ickshonpe Date: Tue, 14 Jan 2025 01:01:31 +0000 Subject: [PATCH] `update_text2d_layout` creates new font atlases when the primary window is closed (#7849) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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::window::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) { 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 --- crates/bevy_text/src/text2d.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 547ed6bfce..062532e377 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -222,7 +222,7 @@ pub fn extract_text2d_sprite( /// [`ResMut>`](Assets) -- 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, + mut last_scale_factor: Local>, // Text items which should be reprocessed again, generally when the font hasn't loaded yet. mut queue: Local, mut textures: ResMut>, @@ -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