1use std::collections::VecDeque;
2
3use makepad_derive_widget::*;
4use crate::{label::*, makepad_draw::*, view::*, widget::*};
5
6live_design! {
7 use link::theme::*;
8 use makepad_draw::shader::std::*;
9 use makepad_widgets::label::LabelBase;
10 use makepad_widgets::view::ViewBase;
11
12 PerformanceLiveGraph = {{PerformanceLiveGraph}} {
13 data_increments: 16.
14 bar_width: 3.
15 graph_label: ""
16 data_y_suffix: ""
17
18 width: 300,
19 height: 65,
20
21 margin: 30,
22 abs_pos: vec2(0, 0)
23
24 show_bg: true,
25 draw_bg: {
26 color: vec4(0.8, 0.8, 0.8, 0.8)
27 }
28
29 view = <ViewBase> {
30 margin: 5,
31 visible: true
32 flow: Right
33 width: Fill,
34 height: Fill,
35
36 graph_label = <LabelBase> {
37 width: Fit
38 align: {x: 0., y: 1.}
39 draw_text: {
40 text_style: <THEME_FONT_REGULAR>{font_size: 8},
41 color: #111
42 }
43 text: ""
44 }
45
46 <ViewBase> { width: Fill, height: Fit }
47
48 current_y_entry = <LabelBase> {
49 width: Fit
50 align: {x: 1., y: 1}
51 draw_text: {
52 text_style: <THEME_FONT_REGULAR>{font_size: 8},
53 color: #111
54 }
55 text: "-"
56 }
57 }
58 }
59
60 PerformanceView = {{PerformanceView}} {
61 width: Fit,
62 height: Fit,
63
64 abs_pos: vec2(0, 0)
65
66 graph = <PerformanceLiveGraph> {
67 graph_label: "Frame time (max in last second)"
68 data_y_suffix: "ms"
69 }
70
71 }
72}
73
74#[derive(Live, Widget)]
75pub struct PerformanceView {
76 #[deref]
77 view: View,
78 #[rust]
79 next_frame: NextFrame,
80 #[rust]
81 next_refresh_at: f64,
82}
83
84impl LiveHook for PerformanceView {
85 fn after_new_from_doc(&mut self, cx: &mut Cx) {
86 self.next_frame = cx.new_next_frame();
87 self.next_refresh_at = 2.0;
88 }
89}
90
91impl Widget for PerformanceView {
92 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
93 let _ = self.view.draw_walk(cx, scope, walk);
94 DrawStep::done()
95 }
96}
97
98impl PerformanceView {
99 pub fn handle_widget(&mut self, cx: &mut Cx, event: &Event) {
100 if let Some(ne) = self.next_frame.is_event(event) {
101 if self.next_refresh_at < ne.time {
102 self.next_refresh_at = ne.time + 0.5;
103
104 if cx.performance_stats.max_frame_times.len() > 0 {
105 let last_sec_data = cx.performance_stats.max_frame_times.iter().take(10);
107 let time = (last_sec_data
108 .max_by(|a, b| a.time_spent.partial_cmp(&b.time_spent).unwrap())
109 .unwrap()
110 .time_spent
111 * 1000.) as i64;
112
113 self.performance_live_graph(id!(graph))
114 .add_y_entry(cx, time);
115 }
116 }
117
118 self.next_frame = cx.new_next_frame();
119 }
120 }
121}
122
123#[derive(Live, Widget)]
124pub struct PerformanceLiveGraph {
125 #[redraw] #[deref]
126 view: View,
127 #[redraw] #[live]
128 draw_graph: DrawColor,
129 #[redraw] #[live]
130 draw_bar: DrawColor,
131 #[rust]
132 data: VecDeque<i64>,
133 #[live]
134 bar_width: f64,
135 #[live]
136 data_increments: i64,
137 #[live]
138 data_y_suffix: String,
139 #[live]
140 graph_label: String,
141}
142
143impl LiveHook for PerformanceLiveGraph {
144 fn after_new_from_doc(&mut self, cx: &mut Cx) {
145 self.label(id!(graph_label))
146 .set_text(cx, &format!("{}", self.graph_label));
147 }
148}
149
150impl Widget for PerformanceLiveGraph {
151 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk: Walk) -> DrawStep {
152 let _ = self.view.draw_walk(cx, scope, walk);
153 let _ = self.draw_walk(cx, walk);
154 DrawStep::done()
155 }
156}
157
158impl PerformanceLiveGraph {
159 pub fn add_y_entry(&mut self, cx: &mut Cx, y_entry: i64) {
160 let frames_shown_count: usize =
161 (self.walk(cx).width.fixed_or_zero() / self.bar_width) as usize;
162 self.data.push_back(y_entry);
163 while self.data.len() >= frames_shown_count {
164 self.data.pop_front();
165 }
166
167 self.label(id!(current_y_entry))
168 .set_text(cx, &format!("{}{}", y_entry, self.data_y_suffix));
169
170 self.redraw(cx);
171 }
172
173 pub fn draw_walk(&mut self, cx: &mut Cx2d, walk: Walk) {
174 let color_increment_lines = vec4(0.5, 0.5, 0.5, 1.);
175 let color_ok = vec4(0.4, 1.0, 0.4, 1.0);
176 let color_warning = vec4(0.9, 0.9, 0.4, 1.0);
177 let color_fatal = vec4(1.0, 0.2, 0.2, 1.0);
178
179 let graph_width = self.walk(cx).width.fixed_or_zero();
180 let graph_height = self.walk(cx).height.fixed_or_zero();
181 let graph_zero_baseline = graph_height - 20.;
182
183 self.label(id!(graph_label))
184 .set_text(cx, &format!("{}", self.graph_label));
185
186 self.draw_graph.begin(cx, walk, Layout::default());
187
188 if self.data_increments > 0 {
190 for i in 0..(graph_height as i64 / self.data_increments) {
191 self.draw_bar.color = color_increment_lines;
192 let rect = Rect {
193 pos: DVec2 {
194 x: 0.,
195 y: -(i * self.data_increments - graph_zero_baseline as i64) as f64,
197 },
198 size: DVec2 {
199 x: graph_width,
200 y: 1.,
201 },
202 };
203 self.draw_bar.draw_rel(cx, rect);
204 }
205 }
206
207 for (i, &y_entry) in self.data.iter().enumerate() {
209 self.draw_bar.color = match y_entry {
210 t if t < self.data_increments => color_ok,
211 t if t >= self.data_increments && t < self.data_increments * 2 => color_warning,
212 _ => color_fatal,
213 };
214
215 let rect = Rect {
216 pos: DVec2 {
217 x: i as f64 * self.bar_width,
218 y: graph_zero_baseline,
219 },
220 size: DVec2 {
221 x: self.bar_width,
222 y: -(y_entry as f64) - 1.,
224 },
225 };
226
227 self.draw_bar.draw_rel(cx, rect);
228 }
229
230 self.draw_graph.end(cx);
231 }
232}
233
234impl PerformanceLiveGraphRef {
235 pub fn add_y_entry(&self, cx: &mut Cx, y_entry: i64) {
236 if let Some(mut inner) = self.borrow_mut() {
237 inner.add_y_entry(cx, y_entry);
238 }
239 }
240}