From 48bad68257a70cc9469d00470590abe75ead4ab5 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 22 Jul 2020 22:26:49 +0200 Subject: [PATCH] [glium] implement reactive run mode --- egui_glium/src/runner.rs | 41 ++++++++++++++++++++++++++++++++------- example_glium/src/main.rs | 34 +++++++++++++++++++++++++++----- example_wasm/src/lib.rs | 5 ----- 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/egui_glium/src/runner.rs b/egui_glium/src/runner.rs index 6072f35a..abbf8f71 100644 --- a/egui_glium/src/runner.rs +++ b/egui_glium/src/runner.rs @@ -8,6 +8,16 @@ use crate::{ const EGUI_MEMORY_KEY: &str = "egui"; const WINDOW_KEY: &str = "window"; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RunMode { + /// Uses `request_animation_frame` to repaint the UI on each display Hz. + /// This is good for games and stuff where you want to run logic at e.g. 60 FPS. + Continuous, + + /// Only repaint when there are animations or input (mouse movement, keyboard input etc). + Reactive, +} + pub trait App { /// Called onced per frame for you to draw the UI. fn ui(&mut self, ui: &mut egui::Ui, runner: &mut Runner); @@ -19,21 +29,31 @@ pub trait App { pub struct Runner { frame_times: egui::MovementTracker, quit: bool, + run_mode: RunMode, } impl Runner { - pub fn new() -> Self { + pub fn new(run_mode: RunMode) -> Self { Self { frame_times: egui::MovementTracker::new(1000, 1.0), quit: false, + run_mode, } } + pub fn run_mode(&self) -> RunMode { + self.run_mode + } + + pub fn set_run_mode(&mut self, run_mode: RunMode) { + self.run_mode = run_mode; + } + pub fn quit(&mut self) { self.quit = true; } - pub fn cpu_usage(&self) -> f32 { + pub fn cpu_time(&self) -> f32 { self.frame_times.average().unwrap_or_default() } @@ -43,7 +63,12 @@ impl Runner { } /// Run an egui app -pub fn run(title: &str, mut persistence: Persistence, mut app: impl App + 'static) -> ! { +pub fn run( + title: &str, + run_mode: RunMode, + mut persistence: Persistence, + mut app: impl App + 'static, +) -> ! { let event_loop = glutin::event_loop::EventLoop::new(); let mut window = glutin::window::WindowBuilder::new() .with_decorations(true) @@ -75,7 +100,7 @@ pub fn run(title: &str, mut persistence: Persistence, mut app: impl App + 'stati // used to keep track of time for animations let start_time = Instant::now(); - let mut runner = Runner::new(); + let mut runner = Runner::new(run_mode); let mut clipboard = init_clipboard(); event_loop.run(move |event, _, control_flow| { @@ -95,16 +120,18 @@ pub fn run(title: &str, mut persistence: Persistence, mut app: impl App + 'stati runner.frame_times.add(raw_input.time, frame_time); painter.paint_jobs(&display, paint_jobs, ctx.texture()); - handle_output(output, &display, clipboard.as_mut()); if runner.quit { *control_flow = glutin::event_loop::ControlFlow::Exit - } else { - display.gl_window().window().request_redraw(); // TODO: only if needed (new events etc) + } else if runner.run_mode() == RunMode::Continuous || output.needs_repaint { + display.gl_window().window().request_redraw(); } + + handle_output(output, &display, clipboard.as_mut()); } glutin::event::Event::WindowEvent { event, .. } => { input_to_egui(event, clipboard.as_mut(), &mut raw_input, control_flow); + display.gl_window().window().request_redraw(); // TODO: maybe only on some events? } glutin::event::Event::LoopDestroyed => { persistence.set_value(WINDOW_KEY, &WindowSettings::from_display(&display)); diff --git a/example_glium/src/main.rs b/example_glium/src/main.rs index 580597fb..6784f5e0 100644 --- a/example_glium/src/main.rs +++ b/example_glium/src/main.rs @@ -1,13 +1,14 @@ #![deny(warnings)] #![warn(clippy::all)] -use egui_glium::{persistence::Persistence, Runner}; +use egui_glium::{persistence::Persistence, RunMode, Runner}; const APP_KEY: &str = "app"; #[derive(Default, serde::Deserialize, serde::Serialize)] struct MyApp { egui_example_app: egui::ExampleApp, + frames_painted: u64, } impl egui_glium::App for MyApp { @@ -25,12 +26,35 @@ impl egui_glium::App for MyApp { ui.add( label!( - "CPU usage: {:.2} ms (excludes painting)", - 1e3 * runner.cpu_usage() + "CPU usage: {:.2} ms / frame (excludes painting)", + 1e3 * runner.cpu_time() ) .text_style(TextStyle::Monospace), ); - ui.add(label!("FPS: {:.1}", runner.fps()).text_style(TextStyle::Monospace)); + + ui.separator(); + + ui.horizontal(|ui| { + let mut run_mode = runner.run_mode(); + ui.label("Run mode:"); + ui.radio_value("Continuous", &mut run_mode, RunMode::Continuous) + .tooltip_text("Repaint everything each frame"); + ui.radio_value("Reactive", &mut run_mode, RunMode::Reactive) + .tooltip_text("Repaint when there are animations or input (e.g. mouse movement)"); + runner.set_run_mode(run_mode); + }); + + if runner.run_mode() == RunMode::Continuous { + ui.add( + label!("Repainting the UI each frame. FPS: {:.1}", runner.fps()) + .text_style(TextStyle::Monospace), + ); + } else { + ui.label("Only running UI code when there are animations or input"); + } + + self.frames_painted += 1; + ui.label(format!("Total frames painted: {}", self.frames_painted)); } fn on_exit(&mut self, persistence: &mut Persistence) { @@ -42,5 +66,5 @@ fn main() { let title = "Egui glium example"; let persistence = Persistence::from_path(".egui_example_glium.json".into()); let app: MyApp = persistence.get_value(APP_KEY).unwrap_or_default(); - egui_glium::run(title, persistence, app); + egui_glium::run(title, RunMode::Reactive, persistence, app); } diff --git a/example_wasm/src/lib.rs b/example_wasm/src/lib.rs index 774960e1..79f98baa 100644 --- a/example_wasm/src/lib.rs +++ b/example_wasm/src/lib.rs @@ -39,11 +39,6 @@ impl MyApp { }); ui.separator(); - ui.label("WebGl painter info:"); - ui.indent("webgl region id", |ui| { - ui.label(&backend.painter_debug_info()); - }); - ui.add( label!( "CPU usage: {:.2} ms / frame (excludes painting)",