diff --git a/crates/bevy_text/src/glyph_brush.rs b/crates/bevy_text/src/glyph_brush.rs index 70fea30322e18..859f3288001c6 100644 --- a/crates/bevy_text/src/glyph_brush.rs +++ b/crates/bevy_text/src/glyph_brush.rs @@ -8,10 +8,11 @@ use glyph_brush_layout::{ BuiltInLineBreaker, FontId, GlyphPositioner, Layout, SectionGeometry, SectionGlyph, SectionText, ToSectionText, }; +use std::ops::Range; use crate::{ - error::TextError, BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, GlyphAtlasInfo, - TextAlignment, TextSettings, YAxisOrientation, + error::TextError, BreakLineOn, Font, FontAtlasSet, FontAtlasWarning, TextAlignment, + TextSettings, YAxisOrientation, }; pub struct GlyphBrush { @@ -64,9 +65,9 @@ impl GlyphBrush { text_settings: &TextSettings, font_atlas_warning: &mut FontAtlasWarning, y_axis_orientation: YAxisOrientation, - ) -> Result, TextError> { + ) -> Result<(Vec, Vec), TextError> { if glyphs.is_empty() { - return Ok(Vec::new()); + return Ok((Vec::new(), Vec::new())); } let sections_data = sections @@ -85,8 +86,12 @@ impl GlyphBrush { .collect::, _>>()?; let text_bounds = compute_text_bounds(&glyphs, |index| §ions_data[index].3); - + let mut glyph_batches = Vec::new(); let mut positioned_glyphs = Vec::new(); + let mut batch_atlas: Option> = None; + let mut batch_start = 0; + let mut end = 0; + for sg in glyphs { let SectionGlyph { section_index: _, @@ -98,7 +103,9 @@ impl GlyphBrush { let glyph_position = glyph.position; let adjust = GlyphPlacementAdjuster::new(&mut glyph); let section_data = sections_data[sg.section_index]; + if let Some(outlined_glyph) = section_data.1.font.outline_glyph(glyph) { + end += 1; let bounds = outlined_glyph.px_bounds(); let handle_font_atlas: Handle = section_data.0.cast_weak(); let font_atlas_set = font_atlas_set_storage @@ -120,6 +127,7 @@ impl GlyphBrush { } let texture_atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + let glyph_rect = texture_atlas.textures[atlas_info.glyph_index]; let size = Vec2::new(glyph_rect.width(), glyph_rect.height()); @@ -135,17 +143,39 @@ impl GlyphBrush { }; let position = adjust.position(Vec2::new(x, y)); - positioned_glyphs.push(PositionedGlyph { position, size, - atlas_info, + glyph_index: atlas_info.glyph_index, section_index: sg.section_index, byte_index, }); + + if let Some(ref last_atlas) = batch_atlas { + if last_atlas.id() != atlas_info.texture_atlas.id() { + glyph_batches.push(PositionedGlyphBatch { + texture_atlas: batch_atlas.unwrap(), + range: batch_start..end, + }); + batch_start = end; + batch_atlas = Some(atlas_info.texture_atlas.clone()); + } + } else { + batch_atlas = Some(atlas_info.texture_atlas.clone()); + } } } - Ok(positioned_glyphs) + + if batch_start != end { + glyph_batches.push(PositionedGlyphBatch { + texture_atlas: batch_atlas.unwrap(), + range: batch_start..end, + }); + } + + glyph_batches.sort_by_key(|batch| batch.texture_atlas.id()); + + Ok((glyph_batches, positioned_glyphs)) } pub fn add_font(&mut self, handle: Handle, font: FontArc) -> FontId { @@ -161,11 +191,17 @@ impl GlyphBrush { pub struct PositionedGlyph { pub position: Vec2, pub size: Vec2, - pub atlas_info: GlyphAtlasInfo, + pub glyph_index: usize, pub section_index: usize, pub byte_index: usize, } +#[derive(Debug, Clone)] +pub struct PositionedGlyphBatch { + pub texture_atlas: Handle, + pub range: Range, +} + #[cfg(feature = "subpixel_glyph_atlas")] struct GlyphPlacementAdjuster; diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index 2d16ce03c3356..d0b67ef4e2b14 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -11,8 +11,8 @@ use glyph_brush_layout::{FontId, GlyphPositioner, SectionGeometry, SectionText}; use crate::{ compute_text_bounds, error::TextError, glyph_brush::GlyphBrush, scale_value, BreakLineOn, Font, - FontAtlasSet, FontAtlasWarning, PositionedGlyph, TextAlignment, TextSection, TextSettings, - YAxisOrientation, + FontAtlasSet, FontAtlasWarning, PositionedGlyph, PositionedGlyphBatch, TextAlignment, + TextSection, TextSettings, YAxisOrientation, }; #[derive(Default, Resource)] @@ -26,6 +26,7 @@ pub struct TextPipeline { /// Contains scaled glyphs and their size. Generated via [`TextPipeline::queue_text`]. #[derive(Component, Clone, Default, Debug)] pub struct TextLayoutInfo { + pub batches: Vec, pub glyphs: Vec, pub size: Vec2, } @@ -87,7 +88,7 @@ impl TextPipeline { let size = compute_text_bounds(§ion_glyphs, |index| &scaled_fonts[index]).size(); - let glyphs = self.brush.process_glyphs( + let (batches, glyphs) = self.brush.process_glyphs( section_glyphs, §ions, font_atlas_set_storage, @@ -99,7 +100,11 @@ impl TextPipeline { y_axis_orientation, )?; - Ok(TextLayoutInfo { glyphs, size }) + Ok(TextLayoutInfo { + batches, + glyphs, + size, + }) } pub fn create_text_measure( diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index a7c72c5d44f86..dfee002b70de8 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -112,30 +112,32 @@ pub fn extract_text2d_sprite( * GlobalTransform::from_translation(alignment_translation.extend(0.)); let mut color = Color::WHITE; let mut current_section = usize::MAX; - for PositionedGlyph { - position, - atlas_info, - section_index, - .. - } in &text_layout_info.glyphs - { - if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); - current_section = *section_index; - } - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); + for batch in &text_layout_info.batches { + let atlas = texture_atlases.get(&batch.texture_atlas).unwrap(); + for PositionedGlyph { + position, + glyph_index, + section_index, + .. + } in &text_layout_info.glyphs[batch.range.start..batch.range.end] + { + if *section_index != current_section { + color = text.sections[*section_index].style.color.as_rgba_linear(); + current_section = *section_index; + } - extracted_sprites.sprites.push(ExtractedSprite { - entity, - transform: transform * GlobalTransform::from_translation(position.extend(0.)), - color, - rect: Some(atlas.textures[atlas_info.glyph_index]), - custom_size: None, - image_handle_id: atlas.texture.id(), - flip_x: false, - flip_y: false, - anchor: Anchor::Center.as_vec(), - }); + extracted_sprites.sprites.push(ExtractedSprite { + entity, + transform: transform * GlobalTransform::from_translation(position.extend(0.)), + color, + rect: Some(atlas.textures[*glyph_index]), + custom_size: None, + image_handle_id: atlas.texture.id(), + flip_x: false, + flip_y: false, + anchor: Anchor::Center.as_vec(), + }); + } } } } diff --git a/crates/bevy_ui/src/render/mod.rs b/crates/bevy_ui/src/render/mod.rs index 43753866dd4e0..574f0aa027216 100644 --- a/crates/bevy_ui/src/render/mod.rs +++ b/crates/bevy_ui/src/render/mod.rs @@ -544,34 +544,36 @@ pub fn extract_text_uinodes( let mut color = Color::WHITE; let mut current_section = usize::MAX; - for PositionedGlyph { - position, - atlas_info, - section_index, - .. - } in &text_layout_info.glyphs - { - if *section_index != current_section { - color = text.sections[*section_index].style.color.as_rgba_linear(); - current_section = *section_index; + + for batch in &text_layout_info.batches { + let atlas = texture_atlases.get(&batch.texture_atlas).unwrap(); + for PositionedGlyph { + position, + glyph_index, + section_index, + .. + } in &text_layout_info.glyphs[batch.range.start..batch.range.end] + { + if *section_index != current_section { + color = text.sections[*section_index].style.color.as_rgba_linear(); + current_section = *section_index; + } + let mut rect = atlas.textures[*glyph_index]; + rect.min *= inverse_scale_factor; + rect.max *= inverse_scale_factor; + extracted_uinodes.uinodes.push(ExtractedUiNode { + stack_index, + transform: transform + * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), + color, + rect, + image: atlas.texture.clone_weak(), + atlas_size: Some(atlas.size * inverse_scale_factor), + clip: clip.map(|clip| clip.clip), + flip_x: false, + flip_y: false, + }); } - let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap(); - - let mut rect = atlas.textures[atlas_info.glyph_index]; - rect.min *= inverse_scale_factor; - rect.max *= inverse_scale_factor; - extracted_uinodes.uinodes.push(ExtractedUiNode { - stack_index, - transform: transform - * Mat4::from_translation(position.extend(0.) * inverse_scale_factor), - color, - rect, - image: atlas.texture.clone_weak(), - atlas_size: Some(atlas.size * inverse_scale_factor), - clip: clip.map(|clip| clip.clip), - flip_x: false, - flip_y: false, - }); } } }