From f09bd8c0794433cab1612157ffa0a4087deb9808 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Sat, 2 May 2020 04:25:21 -0700 Subject: [PATCH] core: Clean up tick/render loop Don't call `render` from `Player::tick`; instead, require the frontends to explicitly call `render` when they wish to redraw. The frontend can query `Player::needs_render` to see if the stage is dirty and needs a redraw. Update desktop and web to use this new method. This fits better with the newer winit event loop model, which requires explicitly calling `request_redraw`, and should avoid spurious renders. --- core/src/player.rs | 23 +++++++++++----------- desktop/src/main.rs | 48 +++++++++++++++++++++++++++++++++------------ web/src/lib.rs | 11 +++++++---- 3 files changed, 54 insertions(+), 28 deletions(-) diff --git a/core/src/player.rs b/core/src/player.rs index e3398dfce..fe873c2df 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -103,6 +103,7 @@ pub struct Player { swf: Arc, is_playing: bool, + needs_render: bool, audio: Audio, renderer: Renderer, @@ -168,6 +169,7 @@ impl Player { swf: movie.clone(), is_playing: false, + needs_render: true, background_color: Color { r: 255, @@ -264,8 +266,6 @@ impl Player { self.global_time += dt as u64; let frame_time = 1000.0 / self.frame_rate; - let needs_render = self.frame_accumulator >= frame_time; - const MAX_FRAMES_PER_TICK: u32 = 5; // Sanity cap on frame tick. let mut frame = 0; while frame < MAX_FRAMES_PER_TICK && self.frame_accumulator >= frame_time { @@ -280,10 +280,6 @@ impl Player { self.frame_accumulator = 0.0; } - if needs_render { - self.render(); - } - self.audio.tick(); } } @@ -314,6 +310,10 @@ impl Player { self.is_playing = v; } + pub fn needs_render(&self) -> bool { + self.needs_render + } + pub fn movie_width(&self) -> u32 { self.movie_width } @@ -333,7 +333,7 @@ impl Player { } pub fn handle_event(&mut self, event: PlayerEvent) { - let mut needs_render = false; + let mut needs_render = self.needs_render; // Update mouse position from mouse events. if let PlayerEvent::MouseMove { x, y } @@ -442,10 +442,7 @@ impl Player { Self::run_actions(avm, context); }); self.is_mouse_down = is_mouse_down; - if needs_render { - // Update display after mouse events. - self.render(); - } + self.needs_render = needs_render; } /// Update dragged object, if any. @@ -568,7 +565,8 @@ impl Player { for mut level in levels { level.run_frame(avm, update_context); } - }) + }); + self.needs_render = true; } pub fn render(&mut self) { @@ -608,6 +606,7 @@ impl Player { self.renderer.draw_letterbox(self.letterbox); self.renderer.end_frame(); + self.needs_render = false; } pub fn audio(&self) -> &Audio { diff --git a/desktop/src/main.rs b/desktop/src/main.rs index 09752edb7..c0abfc377 100644 --- a/desktop/src/main.rs +++ b/desktop/src/main.rs @@ -98,12 +98,31 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { let mut mouse_pos = PhysicalPosition::new(0.0, 0.0); let mut time = Instant::now(); + let mut next_frame_time = Instant::now(); loop { // Poll UI events event_loop.run(move |event, _window_target, control_flow| { - *control_flow = ControlFlow::Wait; match event { winit::event::Event::LoopDestroyed => return, + + // Core loop + winit::event::Event::MainEventsCleared => { + let new_time = Instant::now(); + let dt = new_time.duration_since(time).as_micros(); + if dt > 0 { + time = new_time; + let mut player_lock = player.lock().unwrap(); + player_lock.tick(dt as f64 / 1000.0); + next_frame_time = new_time + player_lock.time_til_next_frame(); + if player_lock.needs_render() { + window.request_redraw(); + } + } + } + + // Render + winit::event::Event::RedrawRequested(_) => player.lock().unwrap().render(), + winit::event::Event::WindowEvent { event, .. } => match event { WindowEvent::Resized(size) => { let mut player_lock = player.lock().unwrap(); @@ -111,6 +130,7 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { player_lock .renderer_mut() .set_viewport_dimensions(size.width, size.height); + window.request_redraw(); } WindowEvent::CursorMoved { position, .. } => { let mut player_lock = player.lock().unwrap(); @@ -120,6 +140,9 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { y: position.y, }; player_lock.handle_event(event); + if player_lock.needs_render() { + window.request_redraw(); + } } WindowEvent::MouseInput { button: MouseButton::Left, @@ -139,10 +162,16 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { } }; player_lock.handle_event(event); + if player_lock.needs_render() { + window.request_redraw(); + } } WindowEvent::CursorLeft { .. } => { let mut player_lock = player.lock().unwrap(); - player_lock.handle_event(ruffle_core::PlayerEvent::MouseLeft) + player_lock.handle_event(ruffle_core::PlayerEvent::MouseLeft); + if player_lock.needs_render() { + window.request_redraw(); + } } WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit, WindowEvent::KeyboardInput { .. } | WindowEvent::ReceivedCharacter(_) => { @@ -154,6 +183,9 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { .handle_event(event) { player_lock.handle_event(event); + if player_lock.needs_render() { + window.request_redraw(); + } } } _ => (), @@ -166,16 +198,8 @@ fn run_player(input_path: PathBuf) -> Result<(), Box> { } // After polling events, sleep the event loop until the next event or the next frame. - if *control_flow == ControlFlow::Wait { - let new_time = Instant::now(); - let dt = new_time.duration_since(time).as_micros(); - if dt > 0 { - time = new_time; - player.lock().unwrap().tick(dt as f64 / 1000.0); - } - - *control_flow = - ControlFlow::WaitUntil(new_time + player.lock().unwrap().time_til_next_frame()); + if *control_flow != ControlFlow::Exit { + *control_flow = ControlFlow::WaitUntil(next_frame_time); } }); } diff --git a/web/src/lib.rs b/web/src/lib.rs index c72d58a89..ee550b6f7 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -414,7 +414,9 @@ impl Ruffle { 0.0 }; - instance.core.lock().unwrap().tick(dt); + let mut core_lock = instance.core.lock().unwrap(); + core_lock.tick(dt); + let mut needs_render = core_lock.needs_render(); // Check for canvas resize. let canvas_width = instance.canvas.client_width(); @@ -439,16 +441,17 @@ impl Ruffle { instance.canvas.set_width(viewport_width); instance.canvas.set_height(viewport_height); - let mut core_lock = instance.core.lock().unwrap(); core_lock.set_viewport_dimensions(viewport_width, viewport_height); core_lock .renderer_mut() .set_viewport_dimensions(viewport_width, viewport_height); // Force a re-render if we resize. - core_lock.render(); + needs_render = true; + } - drop(core_lock); + if needs_render { + core_lock.render(); } // Request next animation frame.