Path: blob/main/crates/bevy_dev_tools/src/frame_time_graph/mod.rs
6598 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::system::{Res, ResMut};6use bevy_math::ops::log2;7use bevy_reflect::TypePath;8use bevy_render::{9render_resource::{AsBindGroup, ShaderType},10storage::ShaderStorageBuffer,11};12use bevy_shader::{Shader, ShaderRef};13use bevy_ui_render::prelude::{UiMaterial, UiMaterialPlugin};1415use crate::fps_overlay::FpsOverlayConfig;1617const FRAME_TIME_GRAPH_SHADER_HANDLE: Handle<Shader> =18uuid_handle!("4e38163a-5782-47a5-af52-d9161472ab59");1920/// Plugin that sets up everything to render the frame time graph material21pub struct FrameTimeGraphPlugin;2223impl Plugin for FrameTimeGraphPlugin {24fn build(&self, app: &mut bevy_app::App) {25load_internal_asset!(26app,27FRAME_TIME_GRAPH_SHADER_HANDLE,28"frame_time_graph.wgsl",29Shader::from_wgsl30);3132// TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/6933if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {34panic!("Requires FrameTimeDiagnosticsPlugin");35// app.add_plugins(FrameTimeDiagnosticsPlugin);36}3738app.add_plugins(UiMaterialPlugin::<FrametimeGraphMaterial>::default())39.add_systems(Update, update_frame_time_values);40}41}4243/// The config values sent to the frame time graph shader44#[derive(Debug, Clone, Copy, ShaderType)]45pub struct FrameTimeGraphConfigUniform {46// minimum expected delta time47dt_min: f32,48// maximum expected delta time49dt_max: f32,50dt_min_log2: f32,51dt_max_log2: f32,52// controls whether or not the bars width are proportional to their delta time53proportional_width: u32,54}5556impl FrameTimeGraphConfigUniform {57/// `proportional_width`: controls whether or not the bars width are proportional to their delta time58pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self {59// we want an upper limit that is above the target otherwise the bars will disappear60let dt_min = 1. / (target_fps * 1.2);61let dt_max = 1. / min_fps;62Self {63dt_min,64dt_max,65dt_min_log2: log2(dt_min),66dt_max_log2: log2(dt_max),67proportional_width: u32::from(proportional_width),68}69}70}7172/// The material used to render the frame time graph ui node73#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]74pub struct FrametimeGraphMaterial {75/// The history of the previous frame times value.76///77/// This should be updated every frame to match the frame time history from the [`DiagnosticsStore`]78#[storage(0, read_only)]79pub values: Handle<ShaderStorageBuffer>, // Vec<f32>,80/// The configuration values used by the shader to control how the graph is rendered81#[uniform(1)]82pub config: FrameTimeGraphConfigUniform,83}8485impl UiMaterial for FrametimeGraphMaterial {86fn fragment_shader() -> ShaderRef {87FRAME_TIME_GRAPH_SHADER_HANDLE.into()88}89}9091/// A system that updates the frame time values sent to the frame time graph92fn update_frame_time_values(93mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,94mut buffers: ResMut<Assets<ShaderStorageBuffer>>,95diagnostics_store: Res<DiagnosticsStore>,96config: Option<Res<FpsOverlayConfig>>,97) {98if !config.is_none_or(|c| c.frame_time_graph_config.enabled) {99return;100}101let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {102return;103};104let frame_times = frame_time105.values()106// convert to millis107.map(|x| *x as f32 / 1000.0)108.collect::<Vec<_>>();109for (_, material) in frame_time_graph_materials.iter_mut() {110let buffer = buffers.get_mut(&material.values).unwrap();111112buffer.set_data(frame_times.clone());113}114}115116117