1
2use {
3 crate::{
4 app::AppData,
5 makepad_widgets::*,
6 },
7 std::{
8 fmt::Write,
9 env,
10 },
11};
12
13live_design!{
14 use link::shaders::*;
15 use link::widgets::*;
16 use link::theme::*;
17
18 ProfilerEventChart = {{ProfilerEventChart}}{
19 height: Fill, width: Fill,
20 draw_bg: { fn pixel(self)->vec4{ return THEME_COLOR_BG_CONTAINER } }
21 draw_line: {
22 fn pixel(self)->vec4{
23 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
24 sdf.rect(
25 1.,
26 1.,
27 self.rect_size.x - 2.0 ,
28 self.rect_size.y - 2.0
29 )
30 sdf.fill_keep(THEME_COLOR_SHADOW)
31 return sdf.result
32 }
33 }
34 draw_item:{
35 fn pixel(self)->vec4{
36 return self.color
37 }
38 }
39 draw_time:{
40 text_style: <THEME_FONT_REGULAR> {
41 line_spacing: (THEME_FONT_WDGT_LINE_SPACING),
42 font_size: (THEME_FONT_SIZE_P)
43 }
44 color: (THEME_COLOR_LABEL_OUTER)
45 }
46 draw_label:{
47 text_style: <THEME_FONT_REGULAR> {
48 line_spacing: (THEME_FONT_WDGT_LINE_SPACING),
49 font_size: (THEME_FONT_SIZE_P)
50 }
51 color: (THEME_COLOR_LABEL_OUTER_DOWN)
52 }
53 }
54
55 pub Profiler = {{Profiler}}{
56 height: Fill, width: Fill
57 <DockToolbar> {
58 content = {
59 running_button = <ToggleFlat> {
60 text: "Running",
61 active: true,
62 icon_walk: { width: 8. }
63 }
64 clear_button = <ButtonFlat> {
65 text: "Clear"
66 icon_walk: { width: 12. }
67
68 draw_icon: {
69 svg_file: dep("crate://self/resources/icons/icon_profiler_clear.svg"),
70 }
71 }
72 <ButtonGroup> {
73 height: Fit
74 flow: Right
75 align: { x: 0.0, y: 0.5 }
76 }
77 <Vr> {}
78 <View> {
79 width: Fit,
80 flow: Right,
81 spacing: 0.,
82 <Pbold> {
83 width: Fit,
84 text: "Last ",
85 margin: 0.,
86 padding: <THEME_MSPACE_V_1> {}
87 draw_text: { color: (THEME_COLOR_D_4) }
88 }
89 <P> {
90 width: Fit,
91 text: "500 ms",
92 margin: 0.,
93 padding: <THEME_MSPACE_V_1> {}
94 draw_text: { color: (THEME_COLOR_D_4) }
95 }
96 }
97 }
98 }
99 <ProfilerEventChart>{ }
100 }
101}
102
103#[derive(Clone)]
104struct TimeRange{
105 start:f64,
106 end: f64
107}
108
109impl TimeRange{
110 fn len(&self)->f64{self.end - self.start}
111 fn shifted(&self, shift:f64)->Self{Self{start:self.start+shift, end:self.end+shift}}
112}
113
114#[derive(Live, LiveHook, Widget)]
115struct ProfilerEventChart{
116 #[walk] walk:Walk,
117 #[redraw] #[live] draw_bg: DrawQuad,
118 #[live] draw_line: DrawQuad,
119 #[live] draw_item: DrawColor,
120 #[live] draw_label: DrawText,
121 #[live] draw_time: DrawText,
122 #[rust(TimeRange{start:0.0, end: 1.0})] time_range: TimeRange,
123 #[rust] time_drag: Option<TimeRange>,
124 #[rust] tmp_label: String,
125}
126
127impl ProfilerEventChart{
128 fn draw_block(&mut self, cx: &mut Cx2d, rect:&Rect, sample_start:f64, sample_end: f64, label:&str, meta:u64){
129 let scale = rect.size.x / self.time_range.len();
130 let xpos = rect.pos.x + (sample_start - self.time_range.start) * scale;
131 let xsize = ((sample_end - sample_start) * scale).max(2.0);
132
133 let pos = dvec2(xpos, rect.pos.y+20.0);
134 let size = dvec2(xsize, 20.0);
135 let rect = Rect{pos,size};
136
137 self.draw_item.draw_abs(cx, rect);
138 self.tmp_label.clear();
139 if meta >0{
140 if sample_end - sample_start > 0.001{
141 write!(&mut self.tmp_label, "{}({meta}) {:.2} ms", label, (sample_end-sample_start)*1000.0).unwrap();
142 }
143 else{
144 write!(&mut self.tmp_label, "{}({meta}) {:.0} µs", label, (sample_end-sample_start)*1000_000.0).unwrap();
145 }
146 }
147 else{
148 if sample_end - sample_start > 0.001{
149 write!(&mut self.tmp_label, "{} {:.2} ms", label, (sample_end-sample_start)*1000.0).unwrap();
150 }
151 else{
152 write!(&mut self.tmp_label, "{} {:.0} µs", label, (sample_end-sample_start)*1000_000.0).unwrap();
153 }
154 }
155
156 if xsize > 10.0{
158 cx.begin_turtle(Walk::abs_rect(rect), Layout::default());
159 self.draw_label.draw_abs(cx, pos+dvec2(2.0,4.0), &self.tmp_label);
160 cx.end_turtle();
161 }
162 }
163}
164
165impl Widget for ProfilerEventChart {
166 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk:Walk)->DrawStep{
167 self.draw_bg.begin(cx, walk, Layout::default());
168 let bm = &scope.data.get::<AppData>().unwrap().build_manager;
169 let mut label = String::new();
170
171 let rect = cx.turtle().rect();
172 if let Some(pss) = bm.profile.values().next(){
173 let scale = rect.size.x / self.time_range.len();
174
175 let mut step_size = 0.008;
176 while self.time_range.len() / step_size > rect.size.x / 80.0{
177 step_size *= 2.0;
178 }
179
180 while self.time_range.len() / step_size < rect.size.x / 80.0{
181 step_size /= 2.0;
182 }
183
184 let mut iter = (self.time_range.start / step_size).floor() * step_size - self.time_range.start;
185 while iter < self.time_range.len(){
186 let xpos = iter * scale;
187 let pos = dvec2(xpos,0.0)+rect.pos;
188 self.draw_line.draw_abs(cx, Rect{pos, size:dvec2(3.0, rect.size.y)});
189 label.clear();
190 write!(&mut label, "{:.3}s", (iter+self.time_range.start)).unwrap();
191 self.draw_time.draw_abs(cx, pos+dvec2(2.0,2.0), &label);
192 iter += step_size;
193 }
194
195 if let Some(first) = pss.event.iter().position(|v| v.end > self.time_range.start){
196 for i in first..pss.event.len(){
198 let sample = &pss.event[i];
199 if sample.start > self.time_range.end{
200 break;
201 }
202 let color = LiveId(0).bytes_append(&sample.event_u32.to_be_bytes()).0 as u32 | 0xff000000;
203 self.draw_item.color = Vec4::from_u32(color);
204 self.draw_block(cx, &rect, sample.start, sample.end, Event::name_from_u32(sample.event_u32), sample.event_meta);
205 }
206 }
207
208 self.draw_item.color = Vec4::from_u32(0x7f7f7fff);
209 if let Some(first) = pss.gpu.iter().position(|v| v.end > self.time_range.start){
210 for i in first..pss.gpu.len(){
212 let sample = &pss.gpu[i];
213 if sample.start > self.time_range.end{
214 break;
215 }
216 self.draw_block(cx, &Rect{
217 pos:rect.pos + dvec2(0.0,25.0),
218 size:rect.size
219 }, sample.start, sample.end, "GPU", 0);
220 }
221 }
222 }
223 self.draw_bg.end(cx);
224 DrawStep::done()
225 }
226
227 fn handle_event(&mut self, cx: &mut Cx, event: &Event, _scope: &mut Scope){
228 match event.hits(cx, self.draw_bg.area()) {
229 Hit::FingerDown(_fe) => {
230 cx.set_key_focus(self.draw_bg.area());
232 self.time_drag = Some(self.time_range.clone());
233 },
234 Hit::FingerMove(fe) => {
235 if let Some(start) = &self.time_drag{
236 let moved = fe.abs_start.x - fe.abs.x;
238 let scale = self.time_range.len() / fe.rect.size.x;
240 let shift_time = moved * scale;
241 self.time_range = start.shifted(shift_time);
242 self.draw_bg.redraw(cx);
243 }
244 }
245 Hit::FingerScroll(e)=>{
246 if e.device.is_mouse(){
247 let zoom = (1.03).powf(e.scroll.y / 150.0);
248 let scale = self.time_range.len() / e.rect.size.x;
249 let time = scale * (e.abs.x - e.rect.pos.x) + self.time_range.start;
250 self.time_range = TimeRange{
251 start: (self.time_range.start - time) * zoom + time,
252 end: (self.time_range.end - time) * zoom + time,
253 };
254 self.draw_bg.redraw(cx);
255 }
256 }
257 Hit::FingerUp(_) => {
258 }
259 _ => ()
260 }
261 }
262}
263
264#[derive(Live, LiveHook, Widget)]
265struct Profiler{
266 #[deref] view:View,
267}
268
269impl WidgetMatchEvent for Profiler{
270 fn handle_actions(&mut self, _cx: &mut Cx, actions: &Actions, scope: &mut Scope ){
271 let _data = scope.data.get_mut::<AppData>().unwrap();
272 if self.button(id!(clear_button)).clicked(&actions){
273
274 crate::log!("CLICK");
275 }
276 }
277}
278
279impl Widget for Profiler {
280 fn draw_walk(&mut self, cx: &mut Cx2d, scope:&mut Scope, walk:Walk)->DrawStep{
281 self.view.draw_walk_all(cx, scope, walk);
282 DrawStep::done()
283 }
284
285 fn handle_event(&mut self, cx: &mut Cx, event: &Event, scope: &mut Scope){
286 self.view.handle_event(cx, event, scope);
287 self.widget_match_event(cx, event, scope);
288 }
289}