Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_dev_tools/src/frame_time_graph/mod.rs
6598 views
1
//! Module containing logic for the frame time graph
2
3
use bevy_app::{Plugin, Update};
4
use bevy_asset::{load_internal_asset, uuid_handle, Asset, Assets, Handle};
5
use bevy_diagnostic::{DiagnosticsStore, FrameTimeDiagnosticsPlugin};
6
use bevy_ecs::system::{Res, ResMut};
7
use bevy_math::ops::log2;
8
use bevy_reflect::TypePath;
9
use bevy_render::{
10
render_resource::{AsBindGroup, ShaderType},
11
storage::ShaderStorageBuffer,
12
};
13
use bevy_shader::{Shader, ShaderRef};
14
use bevy_ui_render::prelude::{UiMaterial, UiMaterialPlugin};
15
16
use crate::fps_overlay::FpsOverlayConfig;
17
18
const FRAME_TIME_GRAPH_SHADER_HANDLE: Handle<Shader> =
19
uuid_handle!("4e38163a-5782-47a5-af52-d9161472ab59");
20
21
/// Plugin that sets up everything to render the frame time graph material
22
pub struct FrameTimeGraphPlugin;
23
24
impl Plugin for FrameTimeGraphPlugin {
25
fn build(&self, app: &mut bevy_app::App) {
26
load_internal_asset!(
27
app,
28
FRAME_TIME_GRAPH_SHADER_HANDLE,
29
"frame_time_graph.wgsl",
30
Shader::from_wgsl
31
);
32
33
// TODO: Use plugin dependencies, see https://github.com/bevyengine/bevy/issues/69
34
if !app.is_plugin_added::<FrameTimeDiagnosticsPlugin>() {
35
panic!("Requires FrameTimeDiagnosticsPlugin");
36
// app.add_plugins(FrameTimeDiagnosticsPlugin);
37
}
38
39
app.add_plugins(UiMaterialPlugin::<FrametimeGraphMaterial>::default())
40
.add_systems(Update, update_frame_time_values);
41
}
42
}
43
44
/// The config values sent to the frame time graph shader
45
#[derive(Debug, Clone, Copy, ShaderType)]
46
pub struct FrameTimeGraphConfigUniform {
47
// minimum expected delta time
48
dt_min: f32,
49
// maximum expected delta time
50
dt_max: f32,
51
dt_min_log2: f32,
52
dt_max_log2: f32,
53
// controls whether or not the bars width are proportional to their delta time
54
proportional_width: u32,
55
}
56
57
impl FrameTimeGraphConfigUniform {
58
/// `proportional_width`: controls whether or not the bars width are proportional to their delta time
59
pub fn new(target_fps: f32, min_fps: f32, proportional_width: bool) -> Self {
60
// we want an upper limit that is above the target otherwise the bars will disappear
61
let dt_min = 1. / (target_fps * 1.2);
62
let dt_max = 1. / min_fps;
63
Self {
64
dt_min,
65
dt_max,
66
dt_min_log2: log2(dt_min),
67
dt_max_log2: log2(dt_max),
68
proportional_width: u32::from(proportional_width),
69
}
70
}
71
}
72
73
/// The material used to render the frame time graph ui node
74
#[derive(AsBindGroup, Asset, TypePath, Debug, Clone)]
75
pub struct FrametimeGraphMaterial {
76
/// The history of the previous frame times value.
77
///
78
/// This should be updated every frame to match the frame time history from the [`DiagnosticsStore`]
79
#[storage(0, read_only)]
80
pub values: Handle<ShaderStorageBuffer>, // Vec<f32>,
81
/// The configuration values used by the shader to control how the graph is rendered
82
#[uniform(1)]
83
pub config: FrameTimeGraphConfigUniform,
84
}
85
86
impl UiMaterial for FrametimeGraphMaterial {
87
fn fragment_shader() -> ShaderRef {
88
FRAME_TIME_GRAPH_SHADER_HANDLE.into()
89
}
90
}
91
92
/// A system that updates the frame time values sent to the frame time graph
93
fn update_frame_time_values(
94
mut frame_time_graph_materials: ResMut<Assets<FrametimeGraphMaterial>>,
95
mut buffers: ResMut<Assets<ShaderStorageBuffer>>,
96
diagnostics_store: Res<DiagnosticsStore>,
97
config: Option<Res<FpsOverlayConfig>>,
98
) {
99
if !config.is_none_or(|c| c.frame_time_graph_config.enabled) {
100
return;
101
}
102
let Some(frame_time) = diagnostics_store.get(&FrameTimeDiagnosticsPlugin::FRAME_TIME) else {
103
return;
104
};
105
let frame_times = frame_time
106
.values()
107
// convert to millis
108
.map(|x| *x as f32 / 1000.0)
109
.collect::<Vec<_>>();
110
for (_, material) in frame_time_graph_materials.iter_mut() {
111
let buffer = buffers.get_mut(&material.values).unwrap();
112
113
buffer.set_data(frame_times.clone());
114
}
115
}
116
117