Path: blob/main/crates/bevy_dev_tools/src/frame_time_graph/mod.rs
9356 views
//! Module containing logic for the frame time graph12use bevy_app::{Plugin, Update};3use bevy_asset::{load_internal_asset, uuid_handle, Asset, Assets, Handle};4use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};5use bevy_ecs::{6schedule::IntoScheduleConfigs,7system::{Res, ResMut},8};9use bevy_math::ops::log2;10use bevy_reflect::TypePath;11use bevy_render::{12render_resource::{AsBindGroup, ShaderType},13storage::ShaderBuffer,14};15use bevy_shader::{Shader, ShaderRef};16use bevy_ui_render::prelude::{UiMaterial, UiMaterialPlugin};1718use crate::fps_overlay::{FpsOverlayConfig, FpsOverlaySystems};1920const FRAME_TIME_GRAPH_SHADER_HANDLE: Handle<Shader> =21uuid_handle!("4e38163a-5782-47a5-af52-d9161472ab59");2223/// Plugin that sets up everything to render the frame time graph material24pub struct FrameTimeGraphPlugin;2526impl Plugin for FrameTimeGraphPlugin {27fn build(&self, app: &mut bevy_app::App) {28load_internal_asset!(29app,30FRAME_TIME_GRAPH_SHADER_HANDLE,31"frame_time_graph.wgsl",32Shader::from_wgsl33);3435// TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/6936if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {37panic!("Requires FrameTimeDiagnosticsPlugin");38// app.add_plugins(FrameTimeDiagnosticsPlugin);39}4041app.add_plugins(UiMaterialPlugin::<FrametimeGraphMaterial>::default())42.add_systems(43Update,44update_frame_time_values.in_set(FpsOverlaySystems::UpdateText),45);46}47}4849/// The config values sent to the frame time graph shader50#[derive(Debug, Clone, Copy, ShaderType)]51pub struct FrameTimeGraphConfigUniform {52// minimum expected delta time53dt_min: f32,54// maximum expected delta time55dt_max: f32,56dt_min_log2: f32,57dt_max_log2: f32,58// controls whether or not the bars width are proportional to their delta time59proportional_width: u32,60}6162impl FrameTimeGraphConfigUniform {63/// `proportional_width`: controls whether or not the bars width are proportional to their delta time64pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self {65// we want an upper limit that is above the target otherwise the bars will disappear66let dt_min = 1. / (target_fps * 1.2);67let dt_max = 1. / min_fps;68Self {69dt_min,70dt_max,71dt_min_log2: log2(dt_min),72dt_max_log2: log2(dt_max),73proportional_width: u32::from(proportional_width),74}75}76}7778/// The material used to render the frame time graph ui node79#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]80pub struct FrametimeGraphMaterial {81/// The history of the previous frame times value.82///83/// This should be updated every frame to match the frame time history from the [`DiagnosticsStore`]84#[storage(0, read_only)]85pub values: Handle<ShaderBuffer>, // Vec<f32>,86/// The configuration values used by the shader to control how the graph is rendered87#[uniform(1)]88pub config: FrameTimeGraphConfigUniform,89}9091impl UiMaterial for FrametimeGraphMaterial {92fn fragment_shader() -> ShaderRef {93FRAME_TIME_GRAPH_SHADER_HANDLE.into()94}95}9697/// A system that updates the frame time values sent to the frame time graph98fn update_frame_time_values(99mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,100mut buffers: ResMut<Assets<ShaderBuffer>>,101diagnostics_store: Res<DiagnosticsStore>,102config: Option<Res<FpsOverlayConfig>>,103) {104if !config.is_none_or(|c| c.frame_time_graph_config.enabled) {105return;106}107let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {108return;109};110let frame_times = frame_time111.values()112// convert to millis113.map(|x| *x as f32 / 1000.0)114.collect::<Vec<_>>();115for (_, material) in frame_time_graph_materials.iter_mut() {116let buffer = buffers.get_mut(&material.values).unwrap();117118buffer.set_data(frame_times.clone());119}120}121122123