mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
Perform text scaling calculations per text, not per glyph (#7819)
This commit is contained in:
parent
a63881905a
commit
e77eb003ec
5 changed files with 130 additions and 61 deletions
10
Cargo.toml
10
Cargo.toml
|
@ -1553,6 +1553,16 @@ description = "Loads an animated fox model and spawns lots of them. Good for tes
|
|||
category = "Stress Tests"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "many_glyphs"
|
||||
path = "examples/stress_tests/many_glyphs.rs"
|
||||
|
||||
[package.metadata.example.many_glyphs]
|
||||
name = "Many Glyphs"
|
||||
description = "Simple benchmark to test text rendering."
|
||||
category = "Stress Tests"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "many_lights"
|
||||
path = "examples/stress_tests/many_lights.rs"
|
||||
|
|
|
@ -23,8 +23,8 @@ use bevy_utils::HashSet;
|
|||
use bevy_window::{PrimaryWindow, Window, WindowScaleFactorChanged};
|
||||
|
||||
use crate::{
|
||||
Font, FontAtlasSet, FontAtlasWarning, Text, TextError, TextLayoutInfo, TextPipeline,
|
||||
TextSettings, YAxisOrientation,
|
||||
Font, FontAtlasSet, FontAtlasWarning, PositionedGlyph, Text, TextError, TextLayoutInfo,
|
||||
TextPipeline, TextSettings, YAxisOrientation,
|
||||
};
|
||||
|
||||
/// The maximum width and height of text. The text will wrap according to the specified size.
|
||||
|
@ -94,48 +94,42 @@ pub fn extract_text2d_sprite(
|
|||
.get_single()
|
||||
.map(|window| window.resolution.scale_factor() as f32)
|
||||
.unwrap_or(1.0);
|
||||
let scaling = GlobalTransform::from_scale(Vec3::splat(scale_factor.recip()));
|
||||
|
||||
for (entity, computed_visibility, text, text_layout_info, anchor, text_transform) in
|
||||
for (entity, computed_visibility, text, text_layout_info, anchor, global_transform) in
|
||||
text2d_query.iter()
|
||||
{
|
||||
if !computed_visibility.is_visible() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let text_glyphs = &text_layout_info.glyphs;
|
||||
let text_anchor = -(anchor.as_vec() + 0.5);
|
||||
let alignment_offset = text_layout_info.size * text_anchor;
|
||||
let alignment_translation = text_layout_info.size * text_anchor;
|
||||
let transform = *global_transform
|
||||
* scaling
|
||||
* GlobalTransform::from_translation(alignment_translation.extend(0.));
|
||||
let mut color = Color::WHITE;
|
||||
let mut current_section = usize::MAX;
|
||||
for text_glyph in text_glyphs {
|
||||
if text_glyph.section_index != current_section {
|
||||
color = text.sections[text_glyph.section_index]
|
||||
.style
|
||||
.color
|
||||
.as_rgba_linear();
|
||||
current_section = text_glyph.section_index;
|
||||
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(&text_glyph.atlas_info.texture_atlas)
|
||||
.unwrap();
|
||||
let handle = atlas.texture.clone_weak();
|
||||
let index = text_glyph.atlas_info.glyph_index;
|
||||
let rect = Some(atlas.textures[index]);
|
||||
|
||||
let glyph_transform =
|
||||
Transform::from_translation((alignment_offset + text_glyph.position).extend(0.));
|
||||
|
||||
let transform = *text_transform
|
||||
* GlobalTransform::from_scale(Vec3::splat(scale_factor.recip()))
|
||||
* glyph_transform;
|
||||
let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
|
||||
|
||||
extracted_sprites.sprites.push(ExtractedSprite {
|
||||
entity,
|
||||
transform,
|
||||
transform: transform * GlobalTransform::from_translation(position.extend(0.)),
|
||||
color,
|
||||
rect,
|
||||
rect: Some(atlas.textures[atlas_info.glyph_index]),
|
||||
custom_size: None,
|
||||
image_handle_id: handle.id(),
|
||||
image_handle_id: atlas.texture.id(),
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
anchor: Anchor::Center.as_vec(),
|
||||
|
|
|
@ -31,7 +31,7 @@ use bevy_sprite::SpriteAssetEvents;
|
|||
#[cfg(feature = "bevy_text")]
|
||||
use bevy_sprite::TextureAtlas;
|
||||
#[cfg(feature = "bevy_text")]
|
||||
use bevy_text::{Text, TextLayoutInfo};
|
||||
use bevy_text::{PositionedGlyph, Text, TextLayoutInfo};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use bevy_utils::FloatOrd;
|
||||
use bevy_utils::HashMap;
|
||||
|
@ -318,52 +318,43 @@ pub fn extract_text_uinodes(
|
|||
.map(|window| window.resolution.scale_factor() as f32)
|
||||
.unwrap_or(1.0);
|
||||
|
||||
let scaling = Mat4::from_scale(Vec3::splat(scale_factor.recip()));
|
||||
|
||||
for (stack_index, entity) in ui_stack.uinodes.iter().enumerate() {
|
||||
if let Ok((uinode, global_transform, text, text_layout_info, visibility, clip)) =
|
||||
uinode_query.get(*entity)
|
||||
{
|
||||
if !visibility.is_visible() {
|
||||
// Skip if not visible or if size is set to zero (e.g. when a parent is set to `Display::None`)
|
||||
if !visibility.is_visible() || uinode.size().x == 0. || uinode.size().y == 0. {
|
||||
continue;
|
||||
}
|
||||
// Skip if size is set to zero (e.g. when a parent is set to `Display::None`)
|
||||
if uinode.size() == Vec2::ZERO {
|
||||
continue;
|
||||
}
|
||||
let text_glyphs = &text_layout_info.glyphs;
|
||||
let alignment_offset = (uinode.size() / -2.0).extend(0.0);
|
||||
|
||||
let transform = global_transform.compute_matrix()
|
||||
* Mat4::from_translation(-0.5 * uinode.size().extend(0.))
|
||||
* scaling;
|
||||
|
||||
let mut color = Color::WHITE;
|
||||
let mut current_section = usize::MAX;
|
||||
for text_glyph in text_glyphs {
|
||||
if text_glyph.section_index != current_section {
|
||||
color = text.sections[text_glyph.section_index]
|
||||
.style
|
||||
.color
|
||||
.as_rgba_linear();
|
||||
current_section = text_glyph.section_index;
|
||||
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(&text_glyph.atlas_info.texture_atlas)
|
||||
.unwrap();
|
||||
let texture = atlas.texture.clone_weak();
|
||||
let index = text_glyph.atlas_info.glyph_index;
|
||||
let rect = atlas.textures[index];
|
||||
let atlas_size = Some(atlas.size);
|
||||
|
||||
// NOTE: Should match `bevy_text::text2d::extract_text2d_sprite`
|
||||
let extracted_transform = global_transform.compute_matrix()
|
||||
* Mat4::from_scale(Vec3::splat(scale_factor.recip()))
|
||||
* Mat4::from_translation(
|
||||
alignment_offset * scale_factor + text_glyph.position.extend(0.),
|
||||
);
|
||||
let atlas = texture_atlases.get(&atlas_info.texture_atlas).unwrap();
|
||||
|
||||
extracted_uinodes.uinodes.push(ExtractedUiNode {
|
||||
stack_index,
|
||||
transform: extracted_transform,
|
||||
transform: transform * Mat4::from_translation(position.extend(0.)),
|
||||
color,
|
||||
rect,
|
||||
image: texture,
|
||||
atlas_size,
|
||||
rect: atlas.textures[atlas_info.glyph_index],
|
||||
image: atlas.texture.clone_weak(),
|
||||
atlas_size: Some(atlas.size),
|
||||
clip: clip.map(|clip| clip.clip),
|
||||
flip_x: false,
|
||||
flip_y: false,
|
||||
|
|
|
@ -299,6 +299,7 @@ Example | Description
|
|||
[Many Buttons](../examples/stress_tests/many_buttons.rs) | Test rendering of many UI elements
|
||||
[Many Cubes](../examples/stress_tests/many_cubes.rs) | Simple benchmark to test per-entity draw overhead. Run with the `sphere` argument to test frustum culling
|
||||
[Many Foxes](../examples/stress_tests/many_foxes.rs) | Loads an animated fox model and spawns lots of them. Good for testing skinned mesh performance. Takes an unsigned integer argument for the number of foxes to spawn. Defaults to 1000
|
||||
[Many Glyphs](../examples/stress_tests/many_glyphs.rs) | Simple benchmark to test text rendering.
|
||||
[Many Lights](../examples/stress_tests/many_lights.rs) | Simple benchmark to test rendering many point lights. Run with `WGPU_SETTINGS_PRIO=webgl2` to restrict to uniform buffers and max 256 lights
|
||||
[Many Sprites](../examples/stress_tests/many_sprites.rs) | Displays many sprites in a grid arrangement! Used for performance testing. Use `--colored` to enable color tinted sprites.
|
||||
[Text Pipeline](../examples/stress_tests/text_pipeline.rs) | Text Pipeline benchmark
|
||||
|
|
73
examples/stress_tests/many_glyphs.rs
Normal file
73
examples/stress_tests/many_glyphs.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
//! Simple text rendering benchmark.
|
||||
//!
|
||||
//! Creates a `Text` with a single `TextSection` containing `100_000` glyphs,
|
||||
//! and renders it with the UI in a white color and with Text2d in a red color.
|
||||
use bevy::{
|
||||
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
|
||||
prelude::*,
|
||||
text::{BreakLineOn, Text2dBounds},
|
||||
window::{PresentMode, WindowPlugin},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||
primary_window: Some(Window {
|
||||
present_mode: PresentMode::Immediate,
|
||||
..default()
|
||||
}),
|
||||
..default()
|
||||
}))
|
||||
.add_plugin(FrameTimeDiagnosticsPlugin::default())
|
||||
.add_plugin(LogDiagnosticsPlugin::default())
|
||||
.add_startup_system(setup)
|
||||
.run();
|
||||
}
|
||||
|
||||
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
|
||||
commands.spawn(Camera2dBundle::default());
|
||||
let mut text = Text {
|
||||
sections: vec![TextSection {
|
||||
value: "0123456789".repeat(10_000),
|
||||
style: TextStyle {
|
||||
font: asset_server.load("fonts/FiraSans-Bold.ttf"),
|
||||
font_size: 4.,
|
||||
color: Color::WHITE,
|
||||
},
|
||||
}],
|
||||
alignment: TextAlignment::Left,
|
||||
linebreak_behaviour: BreakLineOn::AnyCharacter,
|
||||
};
|
||||
|
||||
commands
|
||||
.spawn(NodeBundle {
|
||||
style: Style {
|
||||
flex_basis: Val::Percent(100.),
|
||||
align_items: AlignItems::Center,
|
||||
justify_content: JustifyContent::Center,
|
||||
..default()
|
||||
},
|
||||
..default()
|
||||
})
|
||||
.with_children(|commands| {
|
||||
commands.spawn(TextBundle {
|
||||
text: text.clone(),
|
||||
style: Style {
|
||||
size: Size::width(Val::Px(1000.)),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
});
|
||||
|
||||
text.sections[0].style.color = Color::RED;
|
||||
|
||||
commands.spawn(Text2dBundle {
|
||||
text,
|
||||
text_anchor: bevy::sprite::Anchor::Center,
|
||||
text_2d_bounds: Text2dBounds {
|
||||
size: Vec2::new(1000., f32::INFINITY),
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue