mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 13:20:05 -04:00
Fix: request_repaint_after works even when called from background thread (#2939)
* Refactor repaint logic
* request_repaint_after also fires the request_repaint callback
* Bug fixes
* Add test to egui_demo_app
* build_demo_web: build debug unless --release is specified
* Fix the web backend too
* Run special clippy for wasm, forbidding some types/methods
* Remove wasm_bindgen_check.sh
* Fix typos
* Revert "Remove wasm_bindgen_check.sh"
This reverts commit 92dde25344.
* Only run cranky/clippy once
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
|
||||
//! When making changes to one you often also want to apply it to the other.
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
use winit::event_loop::{
|
||||
ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget,
|
||||
@@ -19,7 +19,12 @@ use super::epi_integration::{self, EpiIntegration};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum UserEvent {
|
||||
RequestRepaint,
|
||||
RequestRepaint {
|
||||
when: Instant,
|
||||
/// What the frame number was when the repaint was _requested_.
|
||||
frame_nr: u64,
|
||||
},
|
||||
|
||||
#[cfg(feature = "accesskit")]
|
||||
AccessKitActionRequest(accesskit_winit::ActionRequestEvent),
|
||||
}
|
||||
@@ -58,6 +63,9 @@ enum EventResult {
|
||||
}
|
||||
|
||||
trait WinitApp {
|
||||
/// The current frame number, as reported by egui.
|
||||
fn frame_nr(&self) -> u64;
|
||||
|
||||
fn is_focused(&self) -> bool;
|
||||
|
||||
fn integration(&self) -> Option<&EpiIntegration>;
|
||||
@@ -66,7 +74,7 @@ trait WinitApp {
|
||||
|
||||
fn save_and_destroy(&mut self);
|
||||
|
||||
fn paint(&mut self) -> EventResult;
|
||||
fn run_ui_and_paint(&mut self) -> EventResult;
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
@@ -136,18 +144,30 @@ fn run_and_return(
|
||||
// See: https://github.com/rust-windowing/winit/issues/987
|
||||
// See: https://github.com/rust-windowing/winit/issues/1619
|
||||
winit::event::Event::RedrawEventsCleared if cfg!(windows) => {
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint()
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint()
|
||||
}
|
||||
winit::event::Event::RedrawRequested(_) if !cfg!(windows) => {
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint()
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint()
|
||||
}
|
||||
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint)
|
||||
| winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint { when, frame_nr }) => {
|
||||
if winit_app.frame_nr() == *frame_nr {
|
||||
log::trace!("UserEvent::RequestRepaint scheduling repaint at {when:?}");
|
||||
EventResult::RepaintAt(*when)
|
||||
} else {
|
||||
log::trace!("Got outdated UserEvent::RequestRepaint");
|
||||
EventResult::Wait // old request - we've already repainted
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||
..
|
||||
}) => EventResult::RepaintNext,
|
||||
}) => {
|
||||
log::trace!("Woke up to check next_repaint_time");
|
||||
EventResult::Wait
|
||||
}
|
||||
|
||||
winit::event::Event::WindowEvent { window_id, .. }
|
||||
if winit_app.window().is_none()
|
||||
@@ -174,8 +194,8 @@ fn run_and_return(
|
||||
log::trace!("Repaint caused by winit::Event: {:?}", event);
|
||||
if cfg!(windows) {
|
||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint();
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint();
|
||||
} else {
|
||||
// Fix for https://github.com/emilk/egui/issues/2425
|
||||
next_repaint_time = Instant::now();
|
||||
@@ -196,18 +216,20 @@ fn run_and_return(
|
||||
}
|
||||
}
|
||||
|
||||
*control_flow = match next_repaint_time.checked_duration_since(Instant::now()) {
|
||||
None => {
|
||||
if let Some(window) = winit_app.window() {
|
||||
window.request_redraw();
|
||||
}
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
ControlFlow::Poll
|
||||
*control_flow = if next_repaint_time <= Instant::now() {
|
||||
if let Some(window) = winit_app.window() {
|
||||
log::trace!("request_redraw");
|
||||
window.request_redraw();
|
||||
}
|
||||
Some(time_until_next_repaint) => {
|
||||
ControlFlow::WaitUntil(Instant::now() + time_until_next_repaint)
|
||||
next_repaint_time = extremely_far_future();
|
||||
ControlFlow::Poll
|
||||
} else {
|
||||
let time_until_next = next_repaint_time.saturating_duration_since(Instant::now());
|
||||
if time_until_next < std::time::Duration::from_secs(10_000) {
|
||||
log::trace!("WaitUntil {time_until_next:?}");
|
||||
}
|
||||
}
|
||||
ControlFlow::WaitUntil(next_repaint_time)
|
||||
};
|
||||
});
|
||||
|
||||
log::debug!("eframe window closed");
|
||||
@@ -239,18 +261,25 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
// See: https://github.com/rust-windowing/winit/issues/987
|
||||
// See: https://github.com/rust-windowing/winit/issues/1619
|
||||
winit::event::Event::RedrawEventsCleared if cfg!(windows) => {
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint()
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint()
|
||||
}
|
||||
winit::event::Event::RedrawRequested(_) if !cfg!(windows) => {
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint()
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint()
|
||||
}
|
||||
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint)
|
||||
| winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint { when, frame_nr }) => {
|
||||
if winit_app.frame_nr() == frame_nr {
|
||||
EventResult::RepaintAt(when)
|
||||
} else {
|
||||
EventResult::Wait // old request - we've already repainted
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||
..
|
||||
}) => EventResult::RepaintNext,
|
||||
}) => EventResult::Wait, // We just woke up to check next_repaint_time
|
||||
|
||||
event => match winit_app.on_event(event_loop, &event) {
|
||||
Ok(event_result) => event_result,
|
||||
@@ -265,8 +294,8 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
EventResult::RepaintNow => {
|
||||
if cfg!(windows) {
|
||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||
next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000);
|
||||
winit_app.paint();
|
||||
next_repaint_time = extremely_far_future();
|
||||
winit_app.run_ui_and_paint();
|
||||
} else {
|
||||
// Fix for https://github.com/emilk/egui/issues/2425
|
||||
next_repaint_time = Instant::now();
|
||||
@@ -286,17 +315,15 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
||||
}
|
||||
}
|
||||
|
||||
*control_flow = match next_repaint_time.checked_duration_since(Instant::now()) {
|
||||
None => {
|
||||
if let Some(window) = winit_app.window() {
|
||||
window.request_redraw();
|
||||
}
|
||||
ControlFlow::Poll
|
||||
*control_flow = if next_repaint_time <= Instant::now() {
|
||||
if let Some(window) = winit_app.window() {
|
||||
window.request_redraw();
|
||||
}
|
||||
Some(time_until_next_repaint) => {
|
||||
ControlFlow::WaitUntil(Instant::now() + time_until_next_repaint)
|
||||
}
|
||||
}
|
||||
next_repaint_time = extremely_far_future();
|
||||
ControlFlow::Poll
|
||||
} else {
|
||||
ControlFlow::WaitUntil(next_repaint_time)
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
@@ -601,8 +628,6 @@ mod glow_integration {
|
||||
// suspends and resumes.
|
||||
app_creator: Option<epi::AppCreator>,
|
||||
is_focused: bool,
|
||||
|
||||
frame_nr: u64,
|
||||
}
|
||||
|
||||
impl GlowWinitApp {
|
||||
@@ -619,7 +644,6 @@ mod glow_integration {
|
||||
running: None,
|
||||
app_creator: Some(app_creator),
|
||||
is_focused: true,
|
||||
frame_nr: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,12 +722,17 @@ mod glow_integration {
|
||||
|
||||
{
|
||||
let event_loop_proxy = self.repaint_proxy.clone();
|
||||
integration.egui_ctx.set_request_repaint_callback(move || {
|
||||
event_loop_proxy
|
||||
.lock()
|
||||
.send_event(UserEvent::RequestRepaint)
|
||||
.ok();
|
||||
});
|
||||
integration
|
||||
.egui_ctx
|
||||
.set_request_repaint_callback(move |info| {
|
||||
log::trace!("request_repaint_callback: {info:?}");
|
||||
let when = Instant::now() + info.after;
|
||||
let frame_nr = info.current_frame_nr;
|
||||
event_loop_proxy
|
||||
.lock()
|
||||
.send_event(UserEvent::RequestRepaint { when, frame_nr })
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
|
||||
let app_creator = std::mem::take(&mut self.app_creator)
|
||||
@@ -734,6 +763,12 @@ mod glow_integration {
|
||||
}
|
||||
|
||||
impl WinitApp for GlowWinitApp {
|
||||
fn frame_nr(&self) -> u64 {
|
||||
self.running
|
||||
.as_ref()
|
||||
.map_or(0, |r| r.integration.egui_ctx.frame_nr())
|
||||
}
|
||||
|
||||
fn is_focused(&self) -> bool {
|
||||
self.is_focused
|
||||
}
|
||||
@@ -756,7 +791,7 @@ mod glow_integration {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self) -> EventResult {
|
||||
fn run_ui_and_paint(&mut self) -> EventResult {
|
||||
if let Some(running) = &mut self.running {
|
||||
#[cfg(feature = "puffin")]
|
||||
puffin::GlobalProfiler::lock().new_frame();
|
||||
@@ -820,7 +855,7 @@ mod glow_integration {
|
||||
|
||||
#[cfg(feature = "__screenshot")]
|
||||
// give it time to settle:
|
||||
if self.frame_nr == 2 {
|
||||
if integration.egui_ctx.frame_nr() == 2 {
|
||||
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
|
||||
assert!(
|
||||
path.ends_with(".png"),
|
||||
@@ -871,8 +906,6 @@ mod glow_integration {
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
||||
self.frame_nr += 1;
|
||||
|
||||
control_flow
|
||||
} else {
|
||||
EventResult::Wait
|
||||
@@ -1150,13 +1183,18 @@ mod wgpu_integration {
|
||||
|
||||
{
|
||||
let event_loop_proxy = self.repaint_proxy.clone();
|
||||
integration.egui_ctx.set_request_repaint_callback(move || {
|
||||
event_loop_proxy
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(UserEvent::RequestRepaint)
|
||||
.ok();
|
||||
});
|
||||
integration
|
||||
.egui_ctx
|
||||
.set_request_repaint_callback(move |info| {
|
||||
log::trace!("request_repaint_callback: {info:?}");
|
||||
let when = Instant::now() + info.after;
|
||||
let frame_nr = info.current_frame_nr;
|
||||
event_loop_proxy
|
||||
.lock()
|
||||
.unwrap()
|
||||
.send_event(UserEvent::RequestRepaint { when, frame_nr })
|
||||
.ok();
|
||||
});
|
||||
}
|
||||
|
||||
let app_creator = std::mem::take(&mut self.app_creator)
|
||||
@@ -1186,6 +1224,12 @@ mod wgpu_integration {
|
||||
}
|
||||
|
||||
impl WinitApp for WgpuWinitApp {
|
||||
fn frame_nr(&self) -> u64 {
|
||||
self.running
|
||||
.as_ref()
|
||||
.map_or(0, |r| r.integration.egui_ctx.frame_nr())
|
||||
}
|
||||
|
||||
fn is_focused(&self) -> bool {
|
||||
self.is_focused
|
||||
}
|
||||
@@ -1214,7 +1258,7 @@ mod wgpu_integration {
|
||||
}
|
||||
}
|
||||
|
||||
fn paint(&mut self) -> EventResult {
|
||||
fn run_ui_and_paint(&mut self) -> EventResult {
|
||||
if let (Some(running), Some(window)) = (&mut self.running, &self.window) {
|
||||
#[cfg(feature = "puffin")]
|
||||
puffin::GlobalProfiler::lock().new_frame();
|
||||
@@ -1433,12 +1477,11 @@ mod wgpu_integration {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg(feature = "wgpu")]
|
||||
pub use wgpu_integration::run_wgpu;
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "macos"))]
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
fn system_theme(window: &winit::window::Window, options: &NativeOptions) -> Option<crate::Theme> {
|
||||
if options.follow_system_theme {
|
||||
window
|
||||
@@ -1449,9 +1492,8 @@ fn system_theme(window: &winit::window::Window, options: &NativeOptions) -> Opti
|
||||
}
|
||||
}
|
||||
|
||||
// Winit only reads the system theme on macOS and Windows.
|
||||
// See: https://github.com/rust-windowing/winit/issues/1549
|
||||
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
|
||||
fn system_theme(_window: &winit::window::Window, _options: &NativeOptions) -> Option<crate::Theme> {
|
||||
None
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
fn extremely_far_future() -> std::time::Instant {
|
||||
std::time::Instant::now() + std::time::Duration::from_secs(10_000_000_000)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user