mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 06:30:19 +00:00
make UI text rendering camera driven (#13697)
# Objective - Fixes #13687 ## Solution - Text rendering in UI is still dependent on the `PrimaryWIndow` - implements #10559 for text rendering There are other parts of UI that are still `PrimaryWindow` dependent, if the changes here are OK I'll apply them everywhere. I'm not a fan of the `EntityHashMap` here to hold the scale factors, but it seems the quick and easy fix ## Testing - Run example `multiple_windows` on a screen with a scale factor different than 1, close the primary window
This commit is contained in:
parent
52215ce072
commit
3d9b1e4025
1 changed files with 75 additions and 60 deletions
|
@ -1,6 +1,9 @@
|
||||||
use crate::{ContentSize, FixedMeasure, Measure, Node, NodeMeasure, UiScale};
|
use crate::{
|
||||||
|
ContentSize, DefaultUiCamera, FixedMeasure, Measure, Node, NodeMeasure, TargetCamera, UiScale,
|
||||||
|
};
|
||||||
use bevy_asset::Assets;
|
use bevy_asset::Assets;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
|
entity::{Entity, EntityHashMap},
|
||||||
prelude::{Component, DetectChanges},
|
prelude::{Component, DetectChanges},
|
||||||
query::With,
|
query::With,
|
||||||
reflect::ReflectComponent,
|
reflect::ReflectComponent,
|
||||||
|
@ -9,13 +12,13 @@ use bevy_ecs::{
|
||||||
};
|
};
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||||
use bevy_render::texture::Image;
|
use bevy_render::{camera::Camera, texture::Image};
|
||||||
use bevy_sprite::TextureAtlasLayout;
|
use bevy_sprite::TextureAtlasLayout;
|
||||||
use bevy_text::{
|
use bevy_text::{
|
||||||
scale_value, BreakLineOn, Font, FontAtlasSets, Text, TextError, TextLayoutInfo,
|
scale_value, BreakLineOn, Font, FontAtlasSets, Text, TextError, TextLayoutInfo,
|
||||||
TextMeasureInfo, TextPipeline, TextSettings, YAxisOrientation,
|
TextMeasureInfo, TextPipeline, TextSettings, YAxisOrientation,
|
||||||
};
|
};
|
||||||
use bevy_window::{PrimaryWindow, Window};
|
use bevy_utils::Entry;
|
||||||
use taffy::style::AvailableSpace;
|
use taffy::style::AvailableSpace;
|
||||||
|
|
||||||
/// Text system flags
|
/// Text system flags
|
||||||
|
@ -112,41 +115,54 @@ fn create_text_measure(
|
||||||
/// A `Measure` is used by the UI's layout algorithm to determine the appropriate amount of space
|
/// A `Measure` is used by the UI's layout algorithm to determine the appropriate amount of space
|
||||||
/// to provide for the text given the fonts, the text itself and the constraints of the layout.
|
/// to provide for the text given the fonts, the text itself and the constraints of the layout.
|
||||||
///
|
///
|
||||||
/// * All measures are regenerated if the primary window's scale factor or [`UiScale`] is changed.
|
/// * Measures are regenerated if the target camera's scale factor (or primary window if no specific target) or [`UiScale`] is changed.
|
||||||
/// * Changes that only modify the colors of a `Text` do not require a new `Measure`. This system
|
/// * Changes that only modify the colors of a `Text` do not require a new `Measure`. This system
|
||||||
/// is only able to detect that a `Text` component has changed and will regenerate the `Measure` on
|
/// is only able to detect that a `Text` component has changed and will regenerate the `Measure` on
|
||||||
/// color changes. This can be expensive, particularly for large blocks of text, and the [`bypass_change_detection`](bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection)
|
/// color changes. This can be expensive, particularly for large blocks of text, and the [`bypass_change_detection`](bevy_ecs::change_detection::DetectChangesMut::bypass_change_detection)
|
||||||
/// method should be called when only changing the `Text`'s colors.
|
/// method should be called when only changing the `Text`'s colors.
|
||||||
pub fn measure_text_system(
|
pub fn measure_text_system(
|
||||||
mut last_scale_factor: Local<f32>,
|
mut last_scale_factors: Local<EntityHashMap<f32>>,
|
||||||
fonts: Res<Assets<Font>>,
|
fonts: Res<Assets<Font>>,
|
||||||
windows: Query<&Window, With<PrimaryWindow>>,
|
camera_query: Query<(Entity, &Camera)>,
|
||||||
|
default_ui_camera: DefaultUiCamera,
|
||||||
ui_scale: Res<UiScale>,
|
ui_scale: Res<UiScale>,
|
||||||
mut text_query: Query<(Ref<Text>, &mut ContentSize, &mut TextFlags), With<Node>>,
|
mut text_query: Query<
|
||||||
|
(
|
||||||
|
Ref<Text>,
|
||||||
|
&mut ContentSize,
|
||||||
|
&mut TextFlags,
|
||||||
|
Option<&TargetCamera>,
|
||||||
|
),
|
||||||
|
With<Node>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
let window_scale_factor = windows
|
let mut scale_factors: EntityHashMap<f32> = EntityHashMap::default();
|
||||||
.get_single()
|
|
||||||
.map(|window| window.resolution.scale_factor())
|
|
||||||
.unwrap_or(1.);
|
|
||||||
|
|
||||||
let scale_factor = ui_scale.0 * window_scale_factor;
|
for (text, content_size, text_flags, camera) in &mut text_query {
|
||||||
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
#[allow(clippy::float_cmp)]
|
else {
|
||||||
if *last_scale_factor == scale_factor {
|
continue;
|
||||||
// scale factor unchanged, only create new measure funcs for modified text
|
};
|
||||||
for (text, content_size, text_flags) in &mut text_query {
|
let scale_factor = match scale_factors.entry(camera_entity) {
|
||||||
if text.is_changed() || text_flags.needs_new_measure_func || content_size.is_added() {
|
Entry::Occupied(entry) => *entry.get(),
|
||||||
create_text_measure(&fonts, scale_factor, text, content_size, text_flags);
|
Entry::Vacant(entry) => *entry.insert(
|
||||||
}
|
camera_query
|
||||||
}
|
.get(camera_entity)
|
||||||
} else {
|
.ok()
|
||||||
// scale factor changed, create new measure funcs for all text
|
.and_then(|(_, c)| c.target_scaling_factor())
|
||||||
*last_scale_factor = scale_factor;
|
.unwrap_or(1.0)
|
||||||
|
* ui_scale.0,
|
||||||
for (text, content_size, text_flags) in &mut text_query {
|
),
|
||||||
|
};
|
||||||
|
if last_scale_factors.get(&camera_entity) != Some(&scale_factor)
|
||||||
|
|| text.is_changed()
|
||||||
|
|| text_flags.needs_new_measure_func
|
||||||
|
|| content_size.is_added()
|
||||||
|
{
|
||||||
create_text_measure(&fonts, scale_factor, text, content_size, text_flags);
|
create_text_measure(&fonts, scale_factor, text, content_size, text_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*last_scale_factors = scale_factors;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
@ -219,49 +235,47 @@ fn queue_text(
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn text_system(
|
pub fn text_system(
|
||||||
mut textures: ResMut<Assets<Image>>,
|
mut textures: ResMut<Assets<Image>>,
|
||||||
mut last_scale_factor: Local<f32>,
|
mut last_scale_factors: Local<EntityHashMap<f32>>,
|
||||||
fonts: Res<Assets<Font>>,
|
fonts: Res<Assets<Font>>,
|
||||||
windows: Query<&Window, With<PrimaryWindow>>,
|
camera_query: Query<(Entity, &Camera)>,
|
||||||
|
default_ui_camera: DefaultUiCamera,
|
||||||
text_settings: Res<TextSettings>,
|
text_settings: Res<TextSettings>,
|
||||||
ui_scale: Res<UiScale>,
|
ui_scale: Res<UiScale>,
|
||||||
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
|
mut texture_atlases: ResMut<Assets<TextureAtlasLayout>>,
|
||||||
mut font_atlas_sets: ResMut<FontAtlasSets>,
|
mut font_atlas_sets: ResMut<FontAtlasSets>,
|
||||||
mut text_pipeline: ResMut<TextPipeline>,
|
mut text_pipeline: ResMut<TextPipeline>,
|
||||||
mut text_query: Query<(Ref<Node>, &Text, &mut TextLayoutInfo, &mut TextFlags)>,
|
mut text_query: Query<(
|
||||||
|
Ref<Node>,
|
||||||
|
&Text,
|
||||||
|
&mut TextLayoutInfo,
|
||||||
|
&mut TextFlags,
|
||||||
|
Option<&TargetCamera>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
// TODO: Support window-independent scaling: https://github.com/bevyengine/bevy/issues/5621
|
let mut scale_factors: EntityHashMap<f32> = EntityHashMap::default();
|
||||||
let window_scale_factor = windows
|
|
||||||
.get_single()
|
|
||||||
.map(|window| window.resolution.scale_factor())
|
|
||||||
.unwrap_or(1.);
|
|
||||||
|
|
||||||
let scale_factor = ui_scale.0 * window_scale_factor;
|
for (node, text, text_layout_info, text_flags, camera) in &mut text_query {
|
||||||
let inverse_scale_factor = scale_factor.recip();
|
let Some(camera_entity) = camera.map(TargetCamera::entity).or(default_ui_camera.get())
|
||||||
if *last_scale_factor == scale_factor {
|
else {
|
||||||
// Scale factor unchanged, only recompute text for modified text nodes
|
continue;
|
||||||
for (node, text, text_layout_info, text_flags) in &mut text_query {
|
};
|
||||||
if node.is_changed() || text_flags.needs_recompute {
|
let scale_factor = match scale_factors.entry(camera_entity) {
|
||||||
queue_text(
|
Entry::Occupied(entry) => *entry.get(),
|
||||||
&fonts,
|
Entry::Vacant(entry) => *entry.insert(
|
||||||
&mut text_pipeline,
|
camera_query
|
||||||
&mut font_atlas_sets,
|
.get(camera_entity)
|
||||||
&mut texture_atlases,
|
.ok()
|
||||||
&mut textures,
|
.and_then(|(_, c)| c.target_scaling_factor())
|
||||||
&text_settings,
|
.unwrap_or(1.0)
|
||||||
scale_factor,
|
* ui_scale.0,
|
||||||
inverse_scale_factor,
|
),
|
||||||
text,
|
};
|
||||||
node,
|
let inverse_scale_factor = scale_factor.recip();
|
||||||
text_flags,
|
|
||||||
text_layout_info,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Scale factor changed, recompute text for all text nodes
|
|
||||||
*last_scale_factor = scale_factor;
|
|
||||||
|
|
||||||
for (node, text, text_layout_info, text_flags) in &mut text_query {
|
if last_scale_factors.get(&camera_entity) != Some(&scale_factor)
|
||||||
|
|| node.is_changed()
|
||||||
|
|| text_flags.needs_recompute
|
||||||
|
{
|
||||||
queue_text(
|
queue_text(
|
||||||
&fonts,
|
&fonts,
|
||||||
&mut text_pipeline,
|
&mut text_pipeline,
|
||||||
|
@ -278,4 +292,5 @@ pub fn text_system(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*last_scale_factors = scale_factors;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue