Add an option to limit the repaint rate in the web runner (#7482)
Co-authored-by: Lucas Meurer <hi@lucasmerlin.me> Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
parent
9db03983dd
commit
ec5bc35c38
|
|
@ -509,6 +509,10 @@ pub struct WebOptions {
|
|||
///
|
||||
/// Defaults to true.
|
||||
pub should_prevent_default: Box<dyn Fn(&egui::Event) -> bool>,
|
||||
|
||||
/// Maximum rate at which to repaint. This can be used to artificially reduce the repaint rate below
|
||||
/// vsync in order to save resources.
|
||||
pub max_fps: Option<u32>,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
|
@ -527,6 +531,8 @@ impl Default for WebOptions {
|
|||
|
||||
should_stop_propagation: Box::new(|_| true),
|
||||
should_prevent_default: Box::new(|_| true),
|
||||
|
||||
max_fps: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,8 @@ impl AppRunner {
|
|||
wgpu_render_state: None,
|
||||
};
|
||||
|
||||
let needs_repaint: std::sync::Arc<NeedRepaint> = Default::default();
|
||||
let needs_repaint: std::sync::Arc<NeedRepaint> =
|
||||
std::sync::Arc::new(NeedRepaint::new(web_options.max_fps));
|
||||
{
|
||||
let needs_repaint = needs_repaint.clone();
|
||||
egui_ctx.set_request_repaint_callback(move |info| {
|
||||
|
|
|
|||
|
|
@ -50,11 +50,20 @@ impl WebInput {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Stores when to do the next repaint.
|
||||
pub(crate) struct NeedRepaint(Mutex<f64>);
|
||||
pub(crate) struct NeedRepaint {
|
||||
/// Time in seconds when the next repaint should happen.
|
||||
next_repaint: Mutex<f64>,
|
||||
|
||||
impl Default for NeedRepaint {
|
||||
fn default() -> Self {
|
||||
Self(Mutex::new(f64::NEG_INFINITY)) // start with a repaint
|
||||
/// Rate limit for repaint. 0 means "unlimited". The rate may still be limited by vsync.
|
||||
max_fps: u32,
|
||||
}
|
||||
|
||||
impl NeedRepaint {
|
||||
pub fn new(max_fps: Option<u32>) -> Self {
|
||||
Self {
|
||||
next_repaint: Mutex::new(f64::NEG_INFINITY), // start with a repaint
|
||||
max_fps: max_fps.unwrap_or(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -62,25 +71,43 @@ impl NeedRepaint {
|
|||
/// Returns the time (in [`now_sec`] scale) when
|
||||
/// we should next repaint.
|
||||
pub fn when_to_repaint(&self) -> f64 {
|
||||
*self.0.lock()
|
||||
*self.next_repaint.lock()
|
||||
}
|
||||
|
||||
/// Unschedule repainting.
|
||||
pub fn clear(&self) {
|
||||
*self.0.lock() = f64::INFINITY;
|
||||
*self.next_repaint.lock() = f64::INFINITY;
|
||||
}
|
||||
|
||||
pub fn repaint_after(&self, num_seconds: f64) {
|
||||
let mut repaint_time = self.0.lock();
|
||||
*repaint_time = repaint_time.min(super::now_sec() + num_seconds);
|
||||
let mut time = super::now_sec() + num_seconds;
|
||||
time = self.round_repaint_time_to_rate(time);
|
||||
let mut repaint_time = self.next_repaint.lock();
|
||||
*repaint_time = repaint_time.min(time);
|
||||
}
|
||||
|
||||
/// Request a repaint. Depending on the presence of rate limiting, this may not be instant.
|
||||
pub fn repaint(&self) {
|
||||
let time = self.round_repaint_time_to_rate(super::now_sec());
|
||||
let mut repaint_time = self.next_repaint.lock();
|
||||
*repaint_time = repaint_time.min(time);
|
||||
}
|
||||
|
||||
pub fn repaint_asap(&self) {
|
||||
*self.next_repaint.lock() = f64::NEG_INFINITY;
|
||||
}
|
||||
|
||||
pub fn needs_repaint(&self) -> bool {
|
||||
self.when_to_repaint() <= super::now_sec()
|
||||
}
|
||||
|
||||
pub fn repaint_asap(&self) {
|
||||
*self.0.lock() = f64::NEG_INFINITY;
|
||||
fn round_repaint_time_to_rate(&self, time: f64) -> f64 {
|
||||
if self.max_fps == 0 {
|
||||
time
|
||||
} else {
|
||||
let interval = 1.0 / self.max_fps as f64;
|
||||
(time / interval).ceil() * interval
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -638,7 +638,7 @@ fn install_mousemove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
|
|||
let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event);
|
||||
let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
|
||||
runner.input.raw.events.push(egui_event);
|
||||
runner.needs_repaint.repaint_asap();
|
||||
runner.needs_repaint.repaint();
|
||||
|
||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||
if should_stop_propagation {
|
||||
|
|
@ -721,7 +721,7 @@ fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(),
|
|||
runner.input.raw.events.push(egui_event);
|
||||
|
||||
push_touches(runner, egui::TouchPhase::Move, &event);
|
||||
runner.needs_repaint.repaint_asap();
|
||||
runner.needs_repaint.repaint();
|
||||
|
||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||
if should_stop_propagation {
|
||||
|
|
@ -834,7 +834,7 @@ fn install_wheel(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsV
|
|||
let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event);
|
||||
runner.input.raw.events.push(egui_event);
|
||||
|
||||
runner.needs_repaint.repaint_asap();
|
||||
runner.needs_repaint.repaint();
|
||||
|
||||
// Use web options to tell if the web event should be propagated to parent elements based on the egui event.
|
||||
if should_stop_propagation {
|
||||
|
|
|
|||
Loading…
Reference in New Issue