makepad_studio/build_manager/
run_view.rs

1use crate::{
2    makepad_draw::*,
3    makepad_widgets::*,
4    makepad_platform::os::cx_stdin::*,
5    build_manager::build_manager::BuildManager,
6};
7
8live_design!{
9    import makepad_draw::shader::std::*;
10    
11    RunView = {{RunView}} {
12        frame_delta: 0.008,
13        draw_app: {
14            texture tex: texture2d
15            instance recompiling: 0.0
16            instance started: 0.0
17            instance tex_scale: vec2(0.0, 0.0),
18            fn pixel(self) -> vec4 {
19                //return vec4(self.max_iter / 1000.0,0.0,0.0,1.0);
20                let fb = sample2d_rt(self.tex, self.pos * self.tex_scale)
21                if fb.r == 1.0 && fb.g == 0.0 && fb.b == 1.0 {
22                    return #2
23                }
24                return mix(#2,mix(fb, #4, self.recompiling * 0.4),self.started);
25            }
26        }
27        animator: {
28            started = {
29                default: off,
30                off = {
31                    from: {all: Forward {duration: 0.05}}
32                    apply: {draw_app: {started: 0.0}}
33                }
34                on = {
35                    from: {all: Forward {duration: 0.05}}
36                    apply: {draw_app: {started: 1.0}}
37                }
38            }
39            recompiling = {
40                default: off,
41                off = {
42                    from: {all: Forward {duration: 0.05}}
43                    apply: {draw_app: {recompiling: 0.0}}
44                }
45                on = {
46                    from: {all: Forward {duration: 0.05}}
47                    apply: {draw_app: {recompiling: 1.0}}
48                }
49            }
50        }
51    }
52}
53
54
55#[derive(Live)]
56pub struct RunView {
57    #[walk] walk: Walk,
58    #[rust] draw_state: DrawStateWrap<Walk>,
59    #[animator] animator: Animator,
60    #[live] tex_scale: Vec2,
61    #[live] draw_app: DrawQuad,
62    #[live] frame_delta: f64,
63    #[rust] last_size: DVec2,
64    #[rust] tick: NextFrame,
65    #[rust] timer: Timer,
66    #[rust(100usize)] redraw_countdown: usize,
67    #[rust] time: f64,
68    #[rust] frame: u64,
69    #[rust] started: bool,
70}
71
72
73impl LiveHook for RunView {
74    fn before_live_design(cx: &mut Cx) {
75        register_widget!(cx, RunView)
76    }
77    
78    fn after_new_from_doc(&mut self, cx: &mut Cx) {
79        self.tick = cx.new_next_frame(); //start_interval(self.frame_delta);
80        self.time = 0.0;
81        self.draw_app.set_texture(0, &cx.null_texture());
82    }
83}
84
85impl RunView {
86    
87    pub fn run_tick(&mut self, cx: &mut Cx, time: f64, run_view_id: LiveId, manager: &mut BuildManager) {
88        self.frame += 1;
89        manager.send_host_to_stdin(run_view_id, HostToStdin::Tick {
90            buffer_id: run_view_id.0,
91            frame: 0,
92            time: time
93        });
94        if self.redraw_countdown>0 {
95            self.redraw_countdown -= 1;
96            self.redraw(cx);
97            self.tick = cx.new_next_frame();
98        }
99        else {
100            self.timer = cx.start_timeout(0.008);
101        }
102    }
103    
104    pub fn pump_event_loop(&mut self, cx: &mut Cx, event: &Event, run_view_id: LiveId, manager: &mut BuildManager) {
105        
106        self.animator_handle_event(cx, event);
107        if let Some(te) = self.timer.is_event(event) {
108            self.run_tick(cx, te.time.unwrap_or(0.0), run_view_id, manager)
109        }
110        if let Some(te) = self.tick.is_event(event) {
111            self.run_tick(cx, te.time, run_view_id, manager)
112        }
113    }
114    
115    pub fn handle_event(&mut self, cx: &mut Cx, event: &Event, run_view_id: LiveId, manager: &mut BuildManager) {
116        
117        self.animator_handle_event(cx, event);
118        
119        // lets send mouse events
120        match event.hits(cx, self.draw_app.area()) {
121            Hit::FingerDown(_) => {
122                cx.set_key_focus(self.draw_app.area());
123            }
124            Hit::KeyDown(e) => {
125                manager.send_host_to_stdin(run_view_id, HostToStdin::KeyDown(e));
126            }
127            Hit::KeyUp(e) => {
128                manager.send_host_to_stdin(run_view_id, HostToStdin::KeyUp(e));
129            }
130            _ => ()
131        }
132        let rect = self.draw_app.area().get_rect(cx);
133        match event {
134            Event::MouseDown(e) => {
135                let rel = e.abs - rect.pos;
136                manager.send_host_to_stdin(run_view_id, HostToStdin::MouseDown(StdinMouseDown {
137                    time: e.time,
138                    x: rel.x,
139                    y: rel.y,
140                    button: e.button,
141                }));
142            }
143            Event::MouseMove(e) => {
144                let rel = e.abs - rect.pos;
145                manager.send_host_to_stdin(run_view_id, HostToStdin::MouseMove(StdinMouseMove {
146                    time: e.time,
147                    x: rel.x,
148                    y: rel.y,
149                }));
150            }
151            Event::MouseUp(e) => {
152                let rel = e.abs - rect.pos;
153                manager.send_host_to_stdin(run_view_id, HostToStdin::MouseUp(StdinMouseUp {
154                    time: e.time,
155                    button: e.button,
156                    x: rel.x,
157                    y: rel.y,
158                }));
159            }
160            Event::Scroll(e) => {
161                let rel = e.abs - rect.pos;
162                manager.send_host_to_stdin(run_view_id, HostToStdin::Scroll(StdinScroll {
163                    is_mouse: e.is_mouse,
164                    time: e.time,
165                    x: rel.x,
166                    y: rel.y,
167                    sx: e.scroll.x,
168                    sy: e.scroll.y
169                }));
170            }
171            _ => ()
172        }
173    }
174    
175    pub fn handle_stdin_to_host(&mut self, cx: &mut Cx, msg: &StdinToHost, run_view_id: LiveId, manager: &mut BuildManager) {
176        match msg {
177            
178            StdinToHost::SetCursor(cursor) => {
179                cx.set_cursor(*cursor)
180            }
181            StdinToHost::ReadyToStart => {
182                self.animator_play(cx, id!(recompiling.off));
183                // cause a resize event to fire
184                self.last_size = Default::default();
185                self.redraw(cx);
186            }
187            &StdinToHost::DrawCompleteAndFlip(presentable_draw) => {
188                if let Some(v) = manager.active.builds.values_mut().find(|v| v.run_view_id == run_view_id) {
189                    // Only allow presenting images in the current host swapchain
190                    // (or the previous one, before any draws on the current one),
191                    // and look them up by their unique IDs, to avoid rendering
192                    // different textures than the ones the client just drew to.
193                    let mut try_present_through = |swapchain: &Option<Swapchain<Texture>>| {
194                        let swapchain = swapchain.as_ref()?;
195                        let drawn = swapchain.get_image(presentable_draw.target_id)?;
196                        self.draw_app.set_texture(0, &drawn.image);
197                        self.draw_app.draw_vars.set_var_instance(cx, id!(tex_scale), &[
198                            (presentable_draw.width as f32) / (swapchain.alloc_width as f32),
199                            (presentable_draw.height as f32) / (swapchain.alloc_height as f32),
200                        ]);
201
202                        if !self.started {
203                            self.started = true;
204                            self.animator_play(cx, id!(started.on));
205                        }
206                        self.redraw_countdown = 20;
207
208                        Some(())
209                    };
210
211                    if try_present_through(&v.swapchain).is_some() {
212                        // The client is now drawing to the current swapchain,
213                        // we can discard any previous one we were stashing.
214                        v.last_swapchain_with_completed_draws = None;
215                    } else {
216                        // New draws to a previous swapchain are fine, just means
217                        // the client hasn't yet drawn on the current swapchain,
218                        // what lets us accept draws is their target `Texture`s.
219                        try_present_through(&v.last_swapchain_with_completed_draws);
220                    }
221                }
222            }
223        }
224    }
225    
226    pub fn redraw(&mut self, cx: &mut Cx) {
227        self.draw_app.redraw(cx);
228    }
229    
230    
231    pub fn resend_framebuffer(&mut self, _cx: &mut Cx) {
232        self.last_size = dvec2(0.0,0.0);
233    }
234    
235    
236    pub fn draw(&mut self, cx: &mut Cx2d, run_view_id: LiveId, manager: &mut BuildManager) {
237        
238        // alright so here we draw em texturezs
239        // pick a texture off the buildstate
240        let dpi_factor = cx.current_dpi_factor();
241        let walk = if let Some(walk) = self.draw_state.get() {walk}else {panic!()};
242        let rect = cx.walk_turtle(walk).dpi_snap(dpi_factor);
243        // lets pixelsnap rect in position and size
244
245        if self.last_size != rect.size {
246            self.last_size = rect.size;
247            self.redraw_countdown = 20;
248
249            // FIXME(eddyb) there's no type or naming scheme that tells apart
250            // DPI-scaled and non-DPI-scaled values (other than float-vs-int).
251            let DVec2 { x: inner_width, y: inner_height } = self.last_size;
252
253            // Try to only send the new geometry information to the client
254            // most of the time, letting it draw on its existing swapchain,
255            // and only replace the swapchain when a larger one is needed.
256            manager.send_host_to_stdin(run_view_id, HostToStdin::WindowGeomChange {
257                dpi_factor,
258                inner_width,
259                inner_height,
260            });
261
262            let min_width = ((inner_width * dpi_factor).ceil() as u32).max(1);
263            let min_height = ((inner_height * dpi_factor).ceil() as u32).max(1);
264            let active_build_needs_new_swapchain = manager.active.builds.values_mut()
265                .find(|v| v.run_view_id == run_view_id)
266                .filter(|v| v.aux_chan_host_endpoint.is_some())
267                .filter(|v| {
268                    v.swapchain.as_ref().map(|swapchain| {
269                        min_width > swapchain.alloc_width || min_height > swapchain.alloc_height
270                    }).unwrap_or(true)
271                });
272            if let Some(v) = active_build_needs_new_swapchain {
273                // HACK(eddyb) there is no check that there were any draws on
274                // the current swapchain, but the absence of an older swapchain
275                // (i.e. `last_swapchain_with_completed_draws`) implies either
276                // zero draws so far, or a draw to the current one discarded it.
277                if v.last_swapchain_with_completed_draws.is_none() {
278                    v.last_swapchain_with_completed_draws = v.swapchain.take();
279                }
280
281                // `Texture`s can be reused, but all `PresentableImageId`s must
282                // be regenerated, to tell apart swapchains when e.g. resizing
283                // constantly, so textures keep getting created and replaced.
284                if let Some(swapchain) = &mut v.swapchain {
285                    for pi in &mut swapchain.presentable_images {
286                        pi.id = cx_stdin::PresentableImageId::alloc();
287                    }
288                }
289                let swapchain = v.swapchain.get_or_insert_with(|| {
290                    Swapchain::new(0, 0).images_map(|_| Texture::new(cx))
291                });
292
293                // Update the swapchain allocated size, rounding it up to
294                // reduce the need for further swapchain recreation.
295                swapchain.alloc_width = min_width.max(64).next_power_of_two();
296                swapchain.alloc_height = min_height.max(64).next_power_of_two();
297
298                // Prepare a version of the swapchain for cross-process sharing.
299                let shared_swapchain = swapchain.images_as_ref().images_map(|pi| {
300                    pi.image.set_desc(cx, TextureDesc {
301                        format: TextureFormat::SharedBGRA(pi.id),
302                        width: Some(swapchain.alloc_width as usize),
303                        height: Some(swapchain.alloc_height as usize),
304                    });
305                    cx.share_texture_for_presentable_image(&pi.image)
306                });
307
308                let shared_swapchain =  {
309                    // FIMXE(eddyb) this could be platform-agnostic if the serializer
310                    // could drive the out-of-band UNIX domain socket messaging itself.
311                    #[cfg(target_os = "linux")] {
312                        shared_swapchain.images_map(|pi| {
313                            pi.send_fds_to_aux_chan(v.aux_chan_host_endpoint.as_ref().unwrap())
314                                .map(|pi| pi.image)
315                        })
316                    }
317                    #[cfg(not(target_os = "linux"))] {
318                        shared_swapchain.images_map(|pi| std::io::Result::Ok(pi.image))
319                    }
320                };
321
322                let mut any_errors = false;
323                for pi in &shared_swapchain.presentable_images {
324                    if let Err(err) = &pi.image {
325                        // FIXME(eddyb) is this recoverable or should the whole
326                        // client be restarted? desyncs can get really bad...
327                        error!("failed to send swapchain image to client: {:?}", err);
328                        any_errors = true;
329                    }
330                }
331
332                if !any_errors {
333                    // Inform the client about the new swapchain it *should* use
334                    // (using older swapchains isn't an error, but the draw calls
335                    // will either be noops, or write to orphaned GPU memory ranges).
336                    manager.send_host_to_stdin(run_view_id, HostToStdin::Swapchain(
337                        shared_swapchain.images_map(|pi| pi.image.unwrap()),
338                    ));
339                }
340            }
341        }
342        
343        self.draw_app.draw_abs(cx, rect);
344    }
345}
346
347impl Widget for RunView {
348    fn walk(&mut self, _cx: &mut Cx) -> Walk {
349        self.walk
350    }
351    
352    fn redraw(&mut self, cx: &mut Cx) {
353        self.draw_app.redraw(cx)
354    }
355    
356    fn draw_walk_widget(&mut self, cx: &mut Cx2d, walk: Walk) -> WidgetDraw {
357        if self.draw_state.begin(cx, walk) {
358            return WidgetDraw::hook_above();
359        }
360        self.draw_state.end();
361        WidgetDraw::done()
362    }
363}
364
365#[derive(Clone, PartialEq, WidgetRef)]
366pub struct RunViewRef(WidgetRef);
367
368impl RunViewRef {
369    
370    pub fn recompile_started(&self, cx: &mut Cx) {
371        if let Some(mut inner) = self.borrow_mut() {
372            inner.animator_play(cx, id!(recompiling.on));
373        }
374    }
375    
376}