From df23b937cc60a81913ee3a06cfa775c4d3014ad2 Mon Sep 17 00:00:00 2001 From: charlotte Date: Fri, 27 Sep 2024 17:00:27 -0700 Subject: [PATCH] Make `CosmicFontSystem` and `SwashCache` pub resources. (#15479) # Objective In nannou, we'd like to be able to access the [outline commands](https://docs.rs/cosmic-text/latest/cosmic_text/struct.SwashCache.html#method.get_outline_commands) from swash, while still benefit from Bevy's management of font assets. ## Solution Make `CosmicFontSystem` and `SwashCache` pub resources. ## Testing Ran some examples. --- crates/bevy_text/src/lib.rs | 4 ++- crates/bevy_text/src/pipeline.rs | 57 +++++++++++++++++-------------- crates/bevy_text/src/text2d.rs | 13 +++++-- crates/bevy_ui/src/layout/mod.rs | 18 +++++++--- crates/bevy_ui/src/widget/text.rs | 17 +++++++-- 5 files changed, 73 insertions(+), 36 deletions(-) diff --git a/crates/bevy_text/src/lib.rs b/crates/bevy_text/src/lib.rs index 136ee7d7e7be6..ee2c28a8e1166 100644 --- a/crates/bevy_text/src/lib.rs +++ b/crates/bevy_text/src/lib.rs @@ -109,7 +109,9 @@ impl Plugin for TextPlugin { .register_type::() .init_asset_loader::() .init_resource::() - .insert_resource(TextPipeline::default()) + .init_resource::() + .init_resource::() + .init_resource::() .add_systems( PostUpdate, ( diff --git a/crates/bevy_text/src/pipeline.rs b/crates/bevy_text/src/pipeline.rs index 10efe23f748da..c969b9dc2d470 100644 --- a/crates/bevy_text/src/pipeline.rs +++ b/crates/bevy_text/src/pipeline.rs @@ -20,8 +20,13 @@ use crate::{ PositionedGlyph, TextBounds, TextSection, YAxisOrientation, }; -/// A wrapper around a [`cosmic_text::FontSystem`] -struct CosmicFontSystem(cosmic_text::FontSystem); +/// A wrapper resource around a [`cosmic_text::FontSystem`] +/// +/// The font system is used to retrieve fonts and their information, including glyph outlines. +/// +/// This resource is updated by the [`TextPipeline`] resource. +#[derive(Resource)] +pub struct CosmicFontSystem(pub cosmic_text::FontSystem); impl Default for CosmicFontSystem { fn default() -> Self { @@ -32,8 +37,13 @@ impl Default for CosmicFontSystem { } } -/// A wrapper around a [`cosmic_text::SwashCache`] -struct SwashCache(cosmic_text::SwashCache); +/// A wrapper resource around a [`cosmic_text::SwashCache`] +/// +/// The swash cache rasterizer is used to rasterize glyphs +/// +/// This resource is updated by the [`TextPipeline`] resource. +#[derive(Resource)] +pub struct SwashCache(pub cosmic_text::SwashCache); impl Default for SwashCache { fn default() -> Self { @@ -48,14 +58,6 @@ impl Default for SwashCache { pub struct TextPipeline { /// Identifies a font [`ID`](cosmic_text::fontdb::ID) by its [`Font`] [`Asset`](bevy_asset::Asset). map_handle_to_font_id: HashMap, (cosmic_text::fontdb::ID, String)>, - /// The font system is used to retrieve fonts and their information, including glyph outlines. - /// - /// See [`cosmic_text::FontSystem`] for more information. - font_system: CosmicFontSystem, - /// The swash cache rasterizer is used to rasterize glyphs - /// - /// See [`cosmic_text::SwashCache`] for more information. - swash_cache: SwashCache, /// Buffered vec for collecting spans. /// /// See [this dark magic](https://users.rust-lang.org/t/how-to-cache-a-vectors-capacity/94478/10). @@ -76,8 +78,9 @@ impl TextPipeline { scale_factor: f64, buffer: &mut CosmicBuffer, alignment: JustifyText, + font_system: &mut CosmicFontSystem, ) -> Result<(), TextError> { - let font_system = &mut self.font_system.0; + let font_system = &mut font_system.0; // return early if the fonts are not loaded yet let mut font_size = 0.; @@ -188,6 +191,8 @@ impl TextPipeline { textures: &mut Assets, y_axis_orientation: YAxisOrientation, buffer: &mut CosmicBuffer, + font_system: &mut CosmicFontSystem, + swash_cache: &mut SwashCache, ) -> Result<(), TextError> { layout_info.glyphs.clear(); layout_info.size = Default::default(); @@ -204,11 +209,10 @@ impl TextPipeline { scale_factor, buffer, text_alignment, + font_system, )?; let box_size = buffer_dimensions(buffer); - let font_system = &mut self.font_system.0; - let swash_cache = &mut self.swash_cache.0; buffer .layout_runs() @@ -250,8 +254,8 @@ impl TextPipeline { font_atlas_set.add_glyph_to_atlas( texture_atlases, textures, - font_system, - swash_cache, + &mut font_system.0, + &mut swash_cache.0, layout_glyph, font_smoothing, ) @@ -300,6 +304,7 @@ impl TextPipeline { linebreak_behavior: BreakLineOn, buffer: &mut CosmicBuffer, text_alignment: JustifyText, + font_system: &mut CosmicFontSystem, ) -> Result { const MIN_WIDTH_CONTENT_BOUNDS: TextBounds = TextBounds::new_horizontal(0.0); @@ -311,12 +316,13 @@ impl TextPipeline { scale_factor, buffer, text_alignment, + font_system, )?; let min_width_content_size = buffer_dimensions(buffer); let max_width_content_size = { - let font_system = &mut self.font_system.0; + let font_system = &mut font_system.0; buffer.set_size(font_system, None, None); buffer_dimensions(buffer) }; @@ -328,11 +334,12 @@ impl TextPipeline { }) } - /// Get a mutable reference to the [`cosmic_text::FontSystem`]. - /// - /// Used internally. - pub fn font_system_mut(&mut self) -> &mut cosmic_text::FontSystem { - &mut self.font_system.0 + /// Returns the [`cosmic_text::fontdb::ID`] for a given [`Font`] asset. + pub fn get_font_id(&self, asset_id: AssetId) -> Option { + self.map_handle_to_font_id + .get(&asset_id) + .cloned() + .map(|(id, _)| id) } } @@ -442,11 +449,11 @@ fn buffer_dimensions(buffer: &Buffer) -> Vec2 { } /// Discards stale data cached in `FontSystem`. -pub(crate) fn trim_cosmic_cache(mut pipeline: ResMut) { +pub(crate) fn trim_cosmic_cache(mut font_system: ResMut) { // A trim age of 2 was found to reduce frame time variance vs age of 1 when tested with dynamic text. // See https://github.com/bevyengine/bevy/pull/15037 // // We assume only text updated frequently benefits from the shape cache (e.g. animated text, or // text that is dynamically measured for UI). - pipeline.font_system_mut().shape_run_cache.trim(2); + font_system.0.shape_run_cache.trim(2); } diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index c514cabae3af4..5b84c0c4a8f84 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -1,6 +1,7 @@ +use crate::pipeline::CosmicFontSystem; use crate::{ - BreakLineOn, CosmicBuffer, Font, FontAtlasSets, PositionedGlyph, Text, TextBounds, TextError, - TextLayoutInfo, TextPipeline, YAxisOrientation, + BreakLineOn, CosmicBuffer, Font, FontAtlasSets, PositionedGlyph, SwashCache, Text, TextBounds, + TextError, TextLayoutInfo, TextPipeline, YAxisOrientation, }; use bevy_asset::Assets; use bevy_color::LinearRgba; @@ -158,6 +159,8 @@ pub fn update_text2d_layout( &mut TextLayoutInfo, &mut CosmicBuffer, )>, + mut font_system: ResMut, + mut swash_cache: ResMut, ) { // We need to consume the entire iterator, hence `last` let factor_changed = scale_factor_changed.read().last().is_some(); @@ -198,6 +201,8 @@ pub fn update_text2d_layout( &mut textures, YAxisOrientation::BottomToTop, buffer.as_mut(), + &mut font_system, + &mut swash_cache, ) { Err(TextError::NoSuchFont) => { // There was an error processing the text layout, let's add this entity to the @@ -274,7 +279,9 @@ mod tests { .init_resource::>() .init_resource::() .init_resource::>() - .insert_resource(TextPipeline::default()) + .init_resource::() + .init_resource::() + .init_resource::() .add_systems( Update, ( diff --git a/crates/bevy_ui/src/layout/mod.rs b/crates/bevy_ui/src/layout/mod.rs index 8f0de1fd35306..ef3f0e24733b4 100644 --- a/crates/bevy_ui/src/layout/mod.rs +++ b/crates/bevy_ui/src/layout/mod.rs @@ -22,7 +22,9 @@ use thiserror::Error; use ui_surface::UiSurface; #[cfg(feature = "bevy_text")] -use bevy_text::{CosmicBuffer, TextPipeline}; +use bevy_text::CosmicBuffer; +#[cfg(feature = "bevy_text")] +use bevy_text::CosmicFontSystem; mod convert; pub mod debug; @@ -124,7 +126,7 @@ pub fn ui_layout_system( Option<&ScrollPosition>, )>, #[cfg(feature = "bevy_text")] mut buffer_query: Query<&mut CosmicBuffer>, - #[cfg(feature = "bevy_text")] mut text_pipeline: ResMut, + #[cfg(feature = "bevy_text")] mut font_system: ResMut, ) { let UiLayoutSystemBuffers { interned_root_nodes, @@ -250,8 +252,6 @@ pub fn ui_layout_system( #[cfg(feature = "bevy_text")] let text_buffers = &mut buffer_query; - #[cfg(feature = "bevy_text")] - let font_system = text_pipeline.font_system_mut(); // clean up removed nodes after syncing children to avoid potential panic (invalid SlotMap key used) ui_surface.remove_entities(removed_components.removed_nodes.read()); @@ -271,7 +271,7 @@ pub fn ui_layout_system( #[cfg(feature = "bevy_text")] text_buffers, #[cfg(feature = "bevy_text")] - font_system, + &mut font_system.0, ); for root in &camera.root_nodes { @@ -523,6 +523,10 @@ mod tests { world.init_resource::(); #[cfg(feature = "bevy_text")] world.init_resource::(); + #[cfg(feature = "bevy_text")] + world.init_resource::(); + #[cfg(feature = "bevy_text")] + world.init_resource::(); // spawn a dummy primary window and camera world.spawn(( @@ -1160,6 +1164,10 @@ mod tests { world.init_resource::(); #[cfg(feature = "bevy_text")] world.init_resource::(); + #[cfg(feature = "bevy_text")] + world.init_resource::(); + #[cfg(feature = "bevy_text")] + world.init_resource::(); // spawn a dummy primary window and camera world.spawn(( diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index e84ec322dc13f..812e4dd1ab952 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -16,8 +16,9 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_render::{camera::Camera, texture::Image}; use bevy_sprite::TextureAtlasLayout; use bevy_text::{ - scale_value, BreakLineOn, CosmicBuffer, Font, FontAtlasSets, JustifyText, Text, TextBounds, - TextError, TextLayoutInfo, TextMeasureInfo, TextPipeline, YAxisOrientation, + scale_value, BreakLineOn, CosmicBuffer, CosmicFontSystem, Font, FontAtlasSets, JustifyText, + SwashCache, Text, TextBounds, TextError, TextLayoutInfo, TextMeasureInfo, TextPipeline, + YAxisOrientation, }; use bevy_utils::{tracing::error, Entry}; use taffy::style::AvailableSpace; @@ -112,6 +113,7 @@ fn create_text_measure( mut text_flags: Mut, buffer: &mut CosmicBuffer, text_alignment: JustifyText, + font_system: &mut CosmicFontSystem, ) { match text_pipeline.create_text_measure( entity, @@ -121,6 +123,7 @@ fn create_text_measure( text.linebreak_behavior, buffer, text_alignment, + font_system, ) { Ok(measure) => { if text.linebreak_behavior == BreakLineOn::NoWrap { @@ -173,6 +176,7 @@ pub fn measure_text_system( With, >, mut text_pipeline: ResMut, + mut font_system: ResMut, ) { scale_factors_buffer.clear(); @@ -208,6 +212,7 @@ pub fn measure_text_system( text_flags, buffer.as_mut(), text_alignment, + &mut font_system, ); } } @@ -229,6 +234,8 @@ fn queue_text( mut text_flags: Mut, text_layout_info: Mut, buffer: &mut CosmicBuffer, + font_system: &mut CosmicFontSystem, + swash_cache: &mut SwashCache, ) { // Skip the text node if it is waiting for a new measure func if !text_flags.needs_new_measure_func { @@ -258,6 +265,8 @@ fn queue_text( textures, YAxisOrientation::TopToBottom, buffer, + font_system, + swash_cache, ) { Err(TextError::NoSuchFont) => { // There was an error processing the text layout, try again next frame @@ -305,6 +314,8 @@ pub fn text_system( Option<&TargetCamera>, &mut CosmicBuffer, )>, + mut font_system: ResMut, + mut swash_cache: ResMut, ) { scale_factors_buffer.clear(); @@ -343,6 +354,8 @@ pub fn text_system( text_flags, text_layout_info, buffer.as_mut(), + &mut font_system, + &mut swash_cache, ); } }