1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! FPS 监控 HUD —— 实时显示帧率、CPU、内存使用情况。
use crate::{
Context, Entity, IntoElement, ParentElement, Render, Styled, StyledExt, Window, div, h_flex,
};
/// FPS 监控状态。
#[derive(Default)]
pub struct FpsHudState {
/// 当前帧率。
pub fps: f64,
/// 帧时间(毫秒)。
pub frame_time_ms: f64,
/// CPU 使用率(百分比)。
pub cpu_usage: f64,
/// 内存使用(MB)。
pub memory_mb: f64,
/// 是否可见。
pub visible: bool,
/// 历史帧率记录。
pub fps_history: Vec<f64>,
}
impl FpsHudState {
/// 更新帧率。
pub fn update_fps(&mut self, fps: f64) {
self.fps = fps;
self.frame_time_ms = if fps > 0.0 { 1000.0 / fps } else { 0.0 };
self.fps_history.push(fps);
if self.fps_history.len() > 60 {
self.fps_history.remove(0);
}
}
/// 更新 CPU 使用率。
pub fn update_cpu(&mut self, usage: f64) {
self.cpu_usage = usage;
}
/// 更新内存使用。
pub fn update_memory(&mut self, mb: f64) {
self.memory_mb = mb;
}
/// 切换可见性。
pub fn toggle(&mut self) {
self.visible = !self.visible;
}
/// 获取平均帧率。
pub fn average_fps(&self) -> f64 {
if self.fps_history.is_empty() {
return 0.0;
}
self.fps_history.iter().sum::<f64>() / self.fps_history.len() as f64
}
}
/// FPS 监控 HUD 组件。
pub struct FpsHud {
state: Entity<FpsHudState>,
}
impl FpsHud {
/// 创建新的 FPS HUD。
pub fn new(state: Entity<FpsHudState>) -> Self {
Self { state }
}
}
impl Render for FpsHud {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let state = self.state.read(cx);
if !state.visible {
return div().into_element();
}
let fps_color = if state.fps >= 55.0 {
crate::green_400()
} else if state.fps >= 30.0 {
crate::yellow_400()
} else {
crate::red_400()
};
let cpu_color = if state.cpu_usage < 50.0 {
crate::green_400()
} else if state.cpu_usage < 80.0 {
crate::yellow_400()
} else {
crate::red_400()
};
div()
.absolute()
.top_2()
.right_2()
.bg(crate::gray_900())
.rounded_md()
.p_3()
.shadow_lg()
.child(
h_flex()
.flex_col()
.gap_1()
.child(
h_flex()
.gap_2()
.items_center()
.child(div().text_xs().text_color(crate::gray_400()).child("FPS"))
.child(
div()
.text_sm()
.font_semibold()
.text_color(fps_color)
.child(format!("{:.1}", state.fps)),
)
.child(
div()
.text_xs()
.text_color(crate::gray_500())
.child(format!("({:.1}ms)", state.frame_time_ms)),
),
)
.child(
h_flex()
.gap_2()
.items_center()
.child(div().text_xs().text_color(crate::gray_400()).child("CPU"))
.child(
div()
.text_sm()
.font_semibold()
.text_color(cpu_color)
.child(format!("{:.1}%", state.cpu_usage)),
),
)
.child(
h_flex()
.gap_2()
.items_center()
.child(div().text_xs().text_color(crate::gray_400()).child("MEM"))
.child(
div()
.text_sm()
.font_semibold()
.text_color(crate::blue_400())
.child(format!("{:.1} MB", state.memory_mb)),
),
),
)
}
}