makepad_studio/
profiler.rs

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 lets draw a clipped piece of text 
157        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                // lets draw the time lines and time text
197                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                // lets draw the time lines and time text
211                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                // ok so we get multiple finger downs
231                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                    // ok so how much did we move?
237                    let moved = fe.abs_start.x - fe.abs.x;
238                    // scale this thing to the time window
239                    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}