mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Trim cosmic-text's shape run cache (#15037)
# Objective - Fixes https://github.com/bevyengine/bevy/pull/14991. The `cosmic-text` shape run cache requires manual cleanup for old text that no longer needs to be cached. ## Solution - Add a system to trim the cache. - Add an `average fps` indicator to the `text_debug` example. ## Testing Tested with `cargo run --example text_debug`. - **No shape run cache**: 82fps with ~1fps variance. - **Shape run cache no trim**: 90-100fps with ~2-4fps variance - **Shape run cache trim age = 1**: 90-100fps with ~2-8fps variance - **Shape run cache trim age = 2**: 90-100fps with ~2-4fps variance - **Shape run cache trim age = 2000**: 80-120fps with ~2-6fps variance The shape run cache seems to increase average FPS but also increases frame time variance (when there is dynamic text).
This commit is contained in:
parent
cacf3929db
commit
fa51e26052
3 changed files with 78 additions and 7 deletions
|
@ -123,7 +123,8 @@ impl Plugin for TextPlugin {
|
|||
.ambiguous_with(CameraUpdateSystem),
|
||||
remove_dropped_font_atlas_sets,
|
||||
),
|
||||
);
|
||||
)
|
||||
.add_systems(Last, trim_cosmic_cache);
|
||||
|
||||
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app.add_systems(
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use bevy_asset::{AssetId, Assets};
|
||||
use bevy_ecs::{component::Component, entity::Entity, reflect::ReflectComponent, system::Resource};
|
||||
use bevy_ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
reflect::ReflectComponent,
|
||||
system::{ResMut, Resource},
|
||||
};
|
||||
use bevy_math::{UVec2, Vec2};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_render::texture::Image;
|
||||
|
@ -407,3 +412,13 @@ fn buffer_dimensions(buffer: &Buffer) -> Vec2 {
|
|||
|
||||
Vec2::new(width.ceil(), height).ceil()
|
||||
}
|
||||
|
||||
/// Discards stale data cached in `FontSystem`.
|
||||
pub(crate) fn trim_cosmic_cache(mut pipeline: ResMut<TextPipeline>) {
|
||||
// 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);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
//! Shows various text layout options.
|
||||
|
||||
use std::{collections::VecDeque, time::Duration};
|
||||
|
||||
use bevy::{
|
||||
color::palettes::css::*,
|
||||
diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin},
|
||||
|
@ -154,7 +156,15 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
builder.spawn((
|
||||
TextBundle::from_sections([
|
||||
TextSection::new(
|
||||
"This text changes in the bottom right",
|
||||
"",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 25.0,
|
||||
..default()
|
||||
},
|
||||
),
|
||||
TextSection::new(
|
||||
"\nThis text changes in the bottom right",
|
||||
TextStyle {
|
||||
font: font.clone(),
|
||||
font_size: 25.0,
|
||||
|
@ -223,10 +233,23 @@ fn infotext_system(mut commands: Commands, asset_server: Res<AssetServer>) {
|
|||
}
|
||||
|
||||
fn change_text_system(
|
||||
mut fps_history: Local<VecDeque<f64>>,
|
||||
mut time_history: Local<VecDeque<Duration>>,
|
||||
time: Res<Time>,
|
||||
diagnostics: Res<DiagnosticsStore>,
|
||||
mut query: Query<&mut Text, With<TextChanges>>,
|
||||
) {
|
||||
time_history.push_front(time.elapsed());
|
||||
time_history.truncate(120);
|
||||
let avg_fps = (time_history.len() as f64)
|
||||
/ (time_history.front().copied().unwrap_or_default()
|
||||
- time_history.back().copied().unwrap_or_default())
|
||||
.as_secs_f64()
|
||||
.max(0.0001);
|
||||
fps_history.push_front(avg_fps);
|
||||
fps_history.truncate(120);
|
||||
let fps_variance = std_deviation(fps_history.make_contiguous()).unwrap_or_default();
|
||||
|
||||
for mut text in &mut query {
|
||||
let mut fps = 0.0;
|
||||
if let Some(fps_diagnostic) = diagnostics.get(&FrameTimeDiagnosticsPlugin::FPS) {
|
||||
|
@ -244,12 +267,44 @@ fn change_text_system(
|
|||
}
|
||||
}
|
||||
|
||||
text.sections[0].value = format!(
|
||||
"This text changes in the bottom right - {fps:.1} fps, {frame_time:.3} ms/frame",
|
||||
text.sections[0].value =
|
||||
format!("{avg_fps:.1} avg fps, {fps_variance:.1} frametime variance",);
|
||||
|
||||
text.sections[1].value = format!(
|
||||
"\nThis text changes in the bottom right - {fps:.1} fps, {frame_time:.3} ms/frame",
|
||||
);
|
||||
|
||||
text.sections[3].value = format!("{fps:.1}");
|
||||
text.sections[4].value = format!("{fps:.1}");
|
||||
|
||||
text.sections[5].value = format!("{frame_time:.3}");
|
||||
text.sections[6].value = format!("{frame_time:.3}");
|
||||
}
|
||||
}
|
||||
|
||||
fn mean(data: &[f64]) -> Option<f64> {
|
||||
let sum = data.iter().sum::<f64>();
|
||||
let count = data.len();
|
||||
|
||||
match count {
|
||||
positive if positive > 0 => Some(sum / count as f64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn std_deviation(data: &[f64]) -> Option<f64> {
|
||||
match (mean(data), data.len()) {
|
||||
(Some(data_mean), count) if count > 0 => {
|
||||
let variance = data
|
||||
.iter()
|
||||
.map(|value| {
|
||||
let diff = data_mean - *value;
|
||||
|
||||
diff * diff
|
||||
})
|
||||
.sum::<f64>()
|
||||
/ count as f64;
|
||||
|
||||
Some(variance.sqrt())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue