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.
This commit is contained in:
charlotte 2024-09-27 17:00:27 -07:00 committed by GitHub
parent 9b4d2de215
commit df23b937cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 73 additions and 36 deletions

View file

@ -109,7 +109,9 @@ impl Plugin for TextPlugin {
.register_type::<TextBounds>()
.init_asset_loader::<FontLoader>()
.init_resource::<FontAtlasSets>()
.insert_resource(TextPipeline::default())
.init_resource::<TextPipeline>()
.init_resource::<CosmicFontSystem>()
.init_resource::<SwashCache>()
.add_systems(
PostUpdate,
(

View file

@ -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<AssetId<Font>, (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<Image>,
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<TextMeasureInfo, TextError> {
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<Font>) -> Option<cosmic_text::fontdb::ID> {
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<TextPipeline>) {
pub(crate) fn trim_cosmic_cache(mut font_system: ResMut<CosmicFontSystem>) {
// 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);
}

View file

@ -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<CosmicFontSystem>,
mut swash_cache: ResMut<SwashCache>,
) {
// 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::<Assets<TextureAtlasLayout>>()
.init_resource::<FontAtlasSets>()
.init_resource::<Events<WindowScaleFactorChanged>>()
.insert_resource(TextPipeline::default())
.init_resource::<TextPipeline>()
.init_resource::<CosmicFontSystem>()
.init_resource::<SwashCache>()
.add_systems(
Update,
(

View file

@ -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<TextPipeline>,
#[cfg(feature = "bevy_text")] mut font_system: ResMut<CosmicFontSystem>,
) {
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::<ManualTextureViews>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::TextPipeline>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::CosmicFontSystem>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::SwashCache>();
// spawn a dummy primary window and camera
world.spawn((
@ -1160,6 +1164,10 @@ mod tests {
world.init_resource::<ManualTextureViews>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::TextPipeline>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::CosmicFontSystem>();
#[cfg(feature = "bevy_text")]
world.init_resource::<bevy_text::SwashCache>();
// spawn a dummy primary window and camera
world.spawn((

View file

@ -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<TextFlags>,
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<Node>,
>,
mut text_pipeline: ResMut<TextPipeline>,
mut font_system: ResMut<CosmicFontSystem>,
) {
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<TextFlags>,
text_layout_info: Mut<TextLayoutInfo>,
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<CosmicFontSystem>,
mut swash_cache: ResMut<SwashCache>,
) {
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,
);
}
}