mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 22:30:03 -04:00
big refractor
egui: * removed FullOutput::repaint_after * now for redraw only request_repaint_callback is used! * now on every Context::request_repaint() will repaint only once * exposed Context::requested_repaint and Context::requested_repaint_last_frame eframe: * now event result is returned as EventResult insted of Vec<EventResult> * fix to many redraw requests ----: * fix egui not waking when a repaint was from other thread * now every thing feels more responsive!
This commit is contained in:
@@ -108,7 +108,7 @@ trait WinitApp {
|
|||||||
|
|
||||||
fn save_and_destroy(&mut self);
|
fn save_and_destroy(&mut self);
|
||||||
|
|
||||||
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> Vec<EventResult>;
|
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> EventResult;
|
||||||
|
|
||||||
fn on_event(
|
fn on_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
@@ -169,6 +169,7 @@ fn run_and_return(
|
|||||||
log::debug!("Entering the winit event loop (run_return)…");
|
log::debug!("Entering the winit event loop (run_return)…");
|
||||||
|
|
||||||
let mut windows_next_repaint_times = HashMap::default();
|
let mut windows_next_repaint_times = HashMap::default();
|
||||||
|
let mut next_repaint_time = Option::<Instant>::None;
|
||||||
|
|
||||||
let mut returned_result = Ok(());
|
let mut returned_result = Ok(());
|
||||||
|
|
||||||
@@ -177,7 +178,7 @@ fn run_and_return(
|
|||||||
|
|
||||||
WINIT_EVENT_LOOP.with(|row_event_loop| *row_event_loop.write() = event_loop);
|
WINIT_EVENT_LOOP.with(|row_event_loop| *row_event_loop.write() = event_loop);
|
||||||
|
|
||||||
let event_results = match &event {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopDestroyed => {
|
||||||
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
// On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name),
|
||||||
// so we need to save state now:
|
// so we need to save state now:
|
||||||
@@ -197,16 +198,17 @@ fn run_and_return(
|
|||||||
frame_nr,
|
frame_nr,
|
||||||
viewport_id,
|
viewport_id,
|
||||||
}) => {
|
}) => {
|
||||||
if winit_app.frame_nr(*viewport_id) == *frame_nr + 1 {
|
let current_frame_nr = winit_app.frame_nr(*viewport_id);
|
||||||
|
if current_frame_nr == *frame_nr || current_frame_nr == *frame_nr + 1 {
|
||||||
log::trace!("UserEvent::RequestRepaint scheduling repaint at {when:?}");
|
log::trace!("UserEvent::RequestRepaint scheduling repaint at {when:?}");
|
||||||
if let Some(window_id) = winit_app.get_window_winit_id(*viewport_id) {
|
if let Some(window_id) = winit_app.get_window_winit_id(*viewport_id) {
|
||||||
vec![EventResult::RepaintAt(window_id, *when)]
|
EventResult::RepaintAt(window_id, *when)
|
||||||
} else {
|
} else {
|
||||||
vec![EventResult::Wait]
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log::trace!("Got outdated UserEvent::RequestRepaint");
|
log::trace!("Got outdated UserEvent::RequestRepaint");
|
||||||
vec![EventResult::Wait] // old request - we've already repainted
|
EventResult::Wait // old request - we've already repainted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +216,7 @@ fn run_and_return(
|
|||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
log::trace!("Woke up to check next_repaint_time");
|
log::trace!("Woke up to check next_repaint_time");
|
||||||
vec![EventResult::Wait]
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
|
|
||||||
winit::event::Event::WindowEvent { window_id, .. }
|
winit::event::Event::WindowEvent { window_id, .. }
|
||||||
@@ -222,69 +224,74 @@ fn run_and_return(
|
|||||||
{
|
{
|
||||||
// This can happen if we close a window, and then reopen a new one,
|
// This can happen if we close a window, and then reopen a new one,
|
||||||
// or if we have multiple windows open.
|
// or if we have multiple windows open.
|
||||||
vec![EventResult::RepaintNext(*window_id)]
|
EventResult::RepaintNext(*window_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
event => match winit_app.on_event(event_loop, event) {
|
event => match winit_app.on_event(event_loop, event) {
|
||||||
Ok(event_result) => vec![event_result],
|
Ok(event_result) => event_result,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
log::error!("Exiting because of error: {err:?} on event {event:?}");
|
log::error!("Exiting because of error: {err:?} on event {event:?}");
|
||||||
returned_result = Err(err);
|
returned_result = Err(err);
|
||||||
vec![EventResult::Exit]
|
EventResult::Exit
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for event_result in event_results {
|
match event_result {
|
||||||
match event_result {
|
EventResult::Wait => {
|
||||||
EventResult::Wait => {
|
control_flow.set_wait();
|
||||||
control_flow.set_wait();
|
}
|
||||||
}
|
EventResult::RepaintNow(window_id) => {
|
||||||
EventResult::RepaintNow(window_id) => {
|
log::trace!("Repaint caused by winit::Event: {:?}", event_result);
|
||||||
log::trace!("Repaint caused by winit::Event: {:?}", event_result);
|
if cfg!(target_os = "windows") {
|
||||||
if cfg!(target_os = "windows") {
|
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
windows_next_repaint_times.remove(&window_id);
|
||||||
windows_next_repaint_times.remove(&window_id);
|
|
||||||
|
|
||||||
winit_app.run_ui_and_paint(window_id);
|
winit_app.run_ui_and_paint(window_id);
|
||||||
} else {
|
} else {
|
||||||
// Fix for https://github.com/emilk/egui/issues/2425
|
// Fix for https://github.com/emilk/egui/issues/2425
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventResult::RepaintNext(window_id) => {
|
|
||||||
log::trace!("Repaint caused by winit::Event: {:?}", event_result);
|
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
}
|
||||||
windows_next_repaint_times.insert(
|
EventResult::RepaintNext(window_id) => {
|
||||||
window_id,
|
log::trace!("Repaint caused by winit::Event: {:?}", event_result);
|
||||||
windows_next_repaint_times
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
.get(&window_id)
|
}
|
||||||
.map_or(repaint_time, |last| (*last).min(repaint_time)),
|
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||||
);
|
windows_next_repaint_times.insert(
|
||||||
}
|
window_id,
|
||||||
EventResult::Exit => {
|
windows_next_repaint_times
|
||||||
log::debug!("Asking to exit event loop…");
|
.get(&window_id)
|
||||||
winit_app.save_and_destroy();
|
.map_or(repaint_time, |last| (*last).min(repaint_time)),
|
||||||
*control_flow = ControlFlow::Exit;
|
);
|
||||||
return;
|
}
|
||||||
}
|
EventResult::Exit => {
|
||||||
|
log::debug!("Asking to exit event loop…");
|
||||||
|
winit_app.save_and_destroy();
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut next_repaint_time = Option::<Instant>::None;
|
// This is for not duplicating redraw requests
|
||||||
for (window_id, repaint_time) in &windows_next_repaint_times.clone() {
|
|
||||||
if *repaint_time <= Instant::now() {
|
use winit::event::Event;
|
||||||
if let Some(window) = winit_app.window(*window_id) {
|
if matches!(
|
||||||
log::trace!("request_redraw");
|
event,
|
||||||
window.read().request_redraw();
|
Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed
|
||||||
|
) {
|
||||||
|
for (window_id, repaint_time) in &windows_next_repaint_times.clone() {
|
||||||
|
if *repaint_time <= Instant::now() {
|
||||||
|
if let Some(window) = winit_app.window(*window_id) {
|
||||||
|
log::trace!("request_redraw");
|
||||||
|
window.read().request_redraw();
|
||||||
|
}
|
||||||
|
control_flow.set_poll();
|
||||||
|
} else {
|
||||||
|
next_repaint_time = Some(
|
||||||
|
next_repaint_time.map_or(*repaint_time, |last| last.min(*repaint_time)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
windows_next_repaint_times.remove(window_id);
|
|
||||||
control_flow.set_poll();
|
|
||||||
} else {
|
|
||||||
next_repaint_time =
|
|
||||||
Some(next_repaint_time.map_or(*repaint_time, |last| last.min(*repaint_time)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,21 +327,22 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
|||||||
log::debug!("Entering the winit event loop (run)…");
|
log::debug!("Entering the winit event loop (run)…");
|
||||||
|
|
||||||
let mut windows_next_repaint_times = HashMap::default();
|
let mut windows_next_repaint_times = HashMap::default();
|
||||||
|
let mut next_repaint_time = Option::<Instant>::None;
|
||||||
|
|
||||||
event_loop.run(move |event, event_loop, control_flow| {
|
event_loop.run(move |event, event_loop, control_flow| {
|
||||||
crate::profile_scope!("winit_event", short_event_description(&event));
|
crate::profile_scope!("winit_event", short_event_description(&event));
|
||||||
|
|
||||||
WINIT_EVENT_LOOP.with(|row_event_loop| *row_event_loop.write() = event_loop);
|
WINIT_EVENT_LOOP.with(|row_event_loop| *row_event_loop.write() = event_loop);
|
||||||
|
|
||||||
let event_results = match event {
|
let event_result = match &event {
|
||||||
winit::event::Event::LoopDestroyed => {
|
winit::event::Event::LoopDestroyed => {
|
||||||
log::debug!("Received Event::LoopDestroyed");
|
log::debug!("Received Event::LoopDestroyed");
|
||||||
vec![EventResult::Exit]
|
EventResult::Exit
|
||||||
}
|
}
|
||||||
|
|
||||||
winit::event::Event::RedrawRequested(window_id) => {
|
winit::event::Event::RedrawRequested(window_id) => {
|
||||||
windows_next_repaint_times.remove(&window_id);
|
windows_next_repaint_times.remove(window_id);
|
||||||
winit_app.run_ui_and_paint(window_id)
|
winit_app.run_ui_and_paint(*window_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
winit::event::Event::UserEvent(UserEvent::RequestRepaint {
|
||||||
@@ -342,75 +350,81 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
|
|||||||
frame_nr,
|
frame_nr,
|
||||||
viewport_id,
|
viewport_id,
|
||||||
}) => {
|
}) => {
|
||||||
if winit_app.frame_nr(viewport_id) == frame_nr + 1 {
|
let current_frame_nr = winit_app.frame_nr(*viewport_id);
|
||||||
if let Some(window_id) = winit_app.get_window_winit_id(viewport_id) {
|
if current_frame_nr == *frame_nr || current_frame_nr == *frame_nr + 1 {
|
||||||
vec![EventResult::RepaintAt(window_id, when)]
|
if let Some(window_id) = winit_app.get_window_winit_id(*viewport_id) {
|
||||||
|
EventResult::RepaintAt(window_id, *when)
|
||||||
} else {
|
} else {
|
||||||
vec![EventResult::Wait]
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![EventResult::Wait] // old request - we've already repainted
|
EventResult::Wait // old request - we've already repainted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached {
|
||||||
..
|
..
|
||||||
}) => vec![EventResult::Wait], // We just woke up to check next_repaint_time
|
}) => EventResult::Wait, // We just woke up to check next_repaint_time
|
||||||
|
|
||||||
event => match winit_app.on_event(event_loop, &event) {
|
event => match winit_app.on_event(event_loop, event) {
|
||||||
Ok(event_result) => vec![event_result],
|
Ok(event_result) => event_result,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!("eframe encountered a fatal error: {err}");
|
panic!("eframe encountered a fatal error: {err}");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
for event_result in event_results {
|
match event_result {
|
||||||
match event_result {
|
EventResult::Wait => {}
|
||||||
EventResult::Wait => {}
|
EventResult::RepaintNow(window_id) => {
|
||||||
EventResult::RepaintNow(window_id) => {
|
if cfg!(target_os = "windows") {
|
||||||
if cfg!(target_os = "windows") {
|
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
||||||
// Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280
|
windows_next_repaint_times.remove(&window_id);
|
||||||
windows_next_repaint_times.remove(&window_id);
|
|
||||||
|
|
||||||
winit_app.run_ui_and_paint(window_id);
|
winit_app.run_ui_and_paint(window_id);
|
||||||
} else {
|
} else {
|
||||||
// Fix for https://github.com/emilk/egui/issues/2425
|
// Fix for https://github.com/emilk/egui/issues/2425
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventResult::RepaintNext(window_id) => {
|
|
||||||
windows_next_repaint_times.insert(window_id, Instant::now());
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
}
|
}
|
||||||
EventResult::RepaintAt(window_id, repaint_time) => {
|
}
|
||||||
windows_next_repaint_times.insert(
|
EventResult::RepaintNext(window_id) => {
|
||||||
window_id,
|
windows_next_repaint_times.insert(window_id, Instant::now());
|
||||||
windows_next_repaint_times
|
}
|
||||||
.get(&window_id)
|
EventResult::RepaintAt(window_id, repaint_time) => {
|
||||||
.map_or(repaint_time, |last| (*last).min(repaint_time)),
|
windows_next_repaint_times.insert(
|
||||||
);
|
window_id,
|
||||||
}
|
windows_next_repaint_times
|
||||||
EventResult::Exit => {
|
.get(&window_id)
|
||||||
log::debug!("Quitting - saving app state…");
|
.map_or(repaint_time, |last| (*last).min(repaint_time)),
|
||||||
winit_app.save_and_destroy();
|
);
|
||||||
#[allow(clippy::exit)]
|
}
|
||||||
std::process::exit(0);
|
EventResult::Exit => {
|
||||||
}
|
log::debug!("Quitting - saving app state…");
|
||||||
|
winit_app.save_and_destroy();
|
||||||
|
#[allow(clippy::exit)]
|
||||||
|
std::process::exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut next_repaint_time = Option::<Instant>::None;
|
// This is for not duplicating redraw requests
|
||||||
for (window_id, repaint_time) in &windows_next_repaint_times.clone() {
|
|
||||||
if *repaint_time <= Instant::now() {
|
use winit::event::Event;
|
||||||
if let Some(window) = winit_app.window(*window_id) {
|
if matches!(
|
||||||
log::trace!("request_redraw");
|
event,
|
||||||
window.read().request_redraw();
|
Event::RedrawEventsCleared | Event::RedrawRequested(_) | Event::Resumed
|
||||||
|
) {
|
||||||
|
for (window_id, repaint_time) in &windows_next_repaint_times.clone() {
|
||||||
|
if *repaint_time <= Instant::now() {
|
||||||
|
if let Some(window) = winit_app.window(*window_id) {
|
||||||
|
log::trace!("request_redraw");
|
||||||
|
window.read().request_redraw();
|
||||||
|
}
|
||||||
|
control_flow.set_poll();
|
||||||
|
} else {
|
||||||
|
next_repaint_time = Some(
|
||||||
|
next_repaint_time.map_or(*repaint_time, |last| last.min(*repaint_time)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
windows_next_repaint_times.remove(window_id);
|
|
||||||
control_flow.set_poll();
|
|
||||||
} else {
|
|
||||||
next_repaint_time =
|
|
||||||
Some(next_repaint_time.map_or(*repaint_time, |last| last.min(*repaint_time)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1356,9 +1370,9 @@ mod glow_integration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> Vec<EventResult> {
|
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> EventResult {
|
||||||
if self.running.read().is_none() {
|
if self.running.read().is_none() {
|
||||||
return vec![EventResult::Wait];
|
return EventResult::Wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(viewport_id) = self.get_window_id(&window_id) {
|
if let Some(viewport_id) = self.get_window_id(&window_id) {
|
||||||
@@ -1387,16 +1401,15 @@ mod glow_integration {
|
|||||||
glutin.viewports.get(&viewport.read().pair.parent)
|
glutin.viewports.get(&viewport.read().pair.parent)
|
||||||
{
|
{
|
||||||
if let Some(window) = parent_viewport.read().window.as_ref() {
|
if let Some(window) = parent_viewport.read().window.as_ref() {
|
||||||
return vec![EventResult::RepaintNext(window.read().id())];
|
return EventResult::RepaintNext(window.read().id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec![];
|
return EventResult::Wait;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let egui::FullOutput {
|
let egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
viewports,
|
viewports,
|
||||||
@@ -1422,7 +1435,6 @@ mod glow_integration {
|
|||||||
let win = &mut *win.write();
|
let win = &mut *win.write();
|
||||||
egui::FullOutput {
|
egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
viewports,
|
viewports,
|
||||||
@@ -1535,37 +1547,12 @@ mod glow_integration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
control_flow = if integration.should_close() {
|
||||||
let glutin = glutin.read();
|
EventResult::Exit
|
||||||
let window_maps = &glutin.window_maps;
|
} else {
|
||||||
|
EventResult::Wait
|
||||||
|
};
|
||||||
|
|
||||||
control_flow = if integration.should_close() {
|
|
||||||
vec![EventResult::Exit]
|
|
||||||
} else {
|
|
||||||
repaint_after
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(id, time)| {
|
|
||||||
if time.is_zero() {
|
|
||||||
window_maps.get(&id).map(|id| EventResult::RepaintNext(*id))
|
|
||||||
} else if let Some(repaint_after_instant) =
|
|
||||||
std::time::Instant::now().checked_add(time)
|
|
||||||
{
|
|
||||||
// if repaint_after is something huge and can't be added to Instant,
|
|
||||||
// we will use `ControlFlow::Wait` instead.
|
|
||||||
// technically, this might lead to some weird corner cases where the user *WANTS*
|
|
||||||
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
|
||||||
// egui backend impl i guess.
|
|
||||||
|
|
||||||
window_maps.get(&id).map(|id| {
|
|
||||||
EventResult::RepaintAt(*id, repaint_after_instant)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<EventResult>>()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
integration
|
integration
|
||||||
.maybe_autosave(app.write().as_mut(), win.read().window.clone().unwrap());
|
.maybe_autosave(app.write().as_mut(), win.read().window.clone().unwrap());
|
||||||
|
|
||||||
@@ -1593,7 +1580,7 @@ mod glow_integration {
|
|||||||
|
|
||||||
control_flow
|
control_flow
|
||||||
} else {
|
} else {
|
||||||
vec![EventResult::Wait]
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2291,7 +2278,7 @@ mod wgpu_integration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> Vec<EventResult> {
|
fn run_ui_and_paint(&mut self, window_id: winit::window::WindowId) -> EventResult {
|
||||||
if let Some(running) = &mut self.running {
|
if let Some(running) = &mut self.running {
|
||||||
#[cfg(feature = "puffin")]
|
#[cfg(feature = "puffin")]
|
||||||
puffin::GlobalProfiler::lock().new_frame();
|
puffin::GlobalProfiler::lock().new_frame();
|
||||||
@@ -2308,22 +2295,24 @@ mod wgpu_integration {
|
|||||||
|
|
||||||
let egui::FullOutput {
|
let egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
mut viewports,
|
mut viewports,
|
||||||
viewport_commands,
|
viewport_commands,
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let Some((viewport_id, Window{window: Some(window), state, render, parent_id })) = windows_id.read().get(&window_id).and_then(|id|(windows.read().get(id).map(|w|(*id, w.clone())))) else{return vec![]};
|
let Some((viewport_id, Window{window: Some(window), state, render, parent_id })) = windows_id.read()
|
||||||
|
.get(&window_id)
|
||||||
|
.and_then(|id|(windows.read().get(id).map(|w|(*id, w.clone()))))
|
||||||
|
else{ return EventResult::Wait };
|
||||||
// This is used to not render a viewport if is sync
|
// This is used to not render a viewport if is sync
|
||||||
if viewport_id != ViewportId::MAIN && render.is_none() {
|
if viewport_id != ViewportId::MAIN && render.is_none() {
|
||||||
if let Some(window) = running.viewports.read().get(&parent_id) {
|
if let Some(window) = running.viewports.read().get(&parent_id) {
|
||||||
if let Some(w) = window.window.as_ref() {
|
if let Some(w) = window.window.as_ref() {
|
||||||
return vec![EventResult::RepaintNext(w.read().id())];
|
return EventResult::RepaintNext(w.read().id());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return vec![];
|
return EventResult::Wait;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = pollster::block_on(
|
let _ = pollster::block_on(
|
||||||
@@ -2334,7 +2323,6 @@ mod wgpu_integration {
|
|||||||
|
|
||||||
egui::FullOutput {
|
egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
viewports,
|
viewports,
|
||||||
@@ -2444,46 +2432,10 @@ mod wgpu_integration {
|
|||||||
.retain(|_, id| active_viewports_ids.contains(id));
|
.retain(|_, id| active_viewports_ids.contains(id));
|
||||||
painter.write().clean_surfaces(&active_viewports_ids);
|
painter.write().clean_surfaces(&active_viewports_ids);
|
||||||
|
|
||||||
let mut control_flow = vec![EventResult::Wait];
|
|
||||||
for repaint_after in repaint_after {
|
|
||||||
control_flow.push(if integration.read().should_close() {
|
|
||||||
EventResult::Exit
|
|
||||||
} else if repaint_after.1.is_zero() {
|
|
||||||
if let Some(Window {
|
|
||||||
window: Some(window),
|
|
||||||
..
|
|
||||||
}) = windows.read().get(&repaint_after.0)
|
|
||||||
{
|
|
||||||
EventResult::RepaintNext(window.read().id())
|
|
||||||
} else {
|
|
||||||
EventResult::Wait
|
|
||||||
}
|
|
||||||
} else if let Some(repaint_after_instant) =
|
|
||||||
std::time::Instant::now().checked_add(repaint_after.1)
|
|
||||||
{
|
|
||||||
// if repaint_after is something huge and can't be added to Instant,
|
|
||||||
// we will use `ControlFlow::Wait` instead.
|
|
||||||
// technically, this might lead to some weird corner cases where the user *WANTS*
|
|
||||||
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
|
|
||||||
// egui backend impl i guess.
|
|
||||||
if let Some(Window {
|
|
||||||
window: Some(window),
|
|
||||||
..
|
|
||||||
}) = windows.read().get(&repaint_after.0)
|
|
||||||
{
|
|
||||||
EventResult::RepaintAt(window.read().id(), repaint_after_instant)
|
|
||||||
} else {
|
|
||||||
EventResult::Wait
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
EventResult::Wait
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some((_, Window{window: Some(window), ..})) = windows_id.read().get(&window_id)
|
let Some((_, Window{window: Some(window), ..})) = windows_id.read().get(&window_id)
|
||||||
.and_then(|id|windows.read().get(id)
|
.and_then(|id|windows.read().get(id)
|
||||||
.map(|w|(*id, w.clone()))
|
.map(|w|(*id, w.clone()))
|
||||||
) else{return vec![]};
|
) else{return EventResult::Wait};
|
||||||
integration
|
integration
|
||||||
.write()
|
.write()
|
||||||
.maybe_autosave(app.as_mut(), window.clone());
|
.maybe_autosave(app.as_mut(), window.clone());
|
||||||
@@ -2495,9 +2447,13 @@ mod wgpu_integration {
|
|||||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||||
}
|
}
|
||||||
|
|
||||||
control_flow
|
if integration.read().should_close() {
|
||||||
|
EventResult::Exit
|
||||||
|
} else {
|
||||||
|
EventResult::Wait
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
vec![EventResult::Wait]
|
EventResult::Wait
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,42 +54,18 @@ impl Default for WrappedTextureManager {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
/// Logic related to repainting the ui.
|
/// Logic related to repainting the ui.
|
||||||
|
#[derive(Default)]
|
||||||
struct Repaint {
|
struct Repaint {
|
||||||
/// The current frame number.
|
/// The current frame number.
|
||||||
///
|
///
|
||||||
/// Incremented at the end of each frame.
|
/// Incremented at the end of each frame.
|
||||||
viewports_frame_nr: HashMap<ViewportId, u64>,
|
viewports_frame_nr: HashMap<ViewportId, u64>,
|
||||||
|
|
||||||
/// The duration backend will poll for new events, before forcing another egui update
|
|
||||||
/// even if there's no new events.
|
|
||||||
///
|
|
||||||
/// Also used to suppress multiple calls to the repaint callback during the same frame.
|
|
||||||
pub repaint_after: HashMap<ViewportId, std::time::Duration>,
|
|
||||||
|
|
||||||
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
/// While positive, keep requesting repaints. Decrement at the end of each frame.
|
||||||
repaint_requests: HashMap<ViewportId, u32>,
|
repaint_request: HashMap<ViewportId, bool>,
|
||||||
request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
|
request_repaint_callback: Option<Box<dyn Fn(RequestRepaintInfo) + Send + Sync>>,
|
||||||
|
|
||||||
requested_repaint_last_frame: bool,
|
requested_repaint_last_frame: HashMap<ViewportId, bool>,
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Repaint {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut repaint_after = HashMap::default();
|
|
||||||
|
|
||||||
// Start with painting an extra frame to compensate for some widgets
|
|
||||||
// that take two frames before they "settle":
|
|
||||||
repaint_after.insert(ViewportId::MAIN, std::time::Duration::from_millis(100));
|
|
||||||
let mut repaint_requests = HashMap::default();
|
|
||||||
repaint_requests.insert(ViewportId::MAIN, 1);
|
|
||||||
Self {
|
|
||||||
viewports_frame_nr: HashMap::default(),
|
|
||||||
repaint_after,
|
|
||||||
repaint_requests,
|
|
||||||
request_repaint_callback: None,
|
|
||||||
requested_repaint_last_frame: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Repaint {
|
impl Repaint {
|
||||||
@@ -99,76 +75,53 @@ impl Repaint {
|
|||||||
|
|
||||||
fn request_repaint_after(&mut self, after: std::time::Duration, viewport_id: ViewportId) {
|
fn request_repaint_after(&mut self, after: std::time::Duration, viewport_id: ViewportId) {
|
||||||
if after == std::time::Duration::ZERO {
|
if after == std::time::Duration::ZERO {
|
||||||
// Do a few extra frames to let things settle.
|
// This will only work if the current viewport is drawing
|
||||||
// This is a bit of a hack, and we don't support it for `repaint_after` callbacks yet.
|
self.repaint_request.insert(viewport_id, true);
|
||||||
self.repaint_requests.insert(viewport_id, 2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We only re-call the callback if we get a lower duration,
|
// This will work always
|
||||||
// otherwise it's already been covered by the previous callback.
|
if let Some(callback) = &self.request_repaint_callback {
|
||||||
if after
|
let info = RequestRepaintInfo {
|
||||||
< self
|
after,
|
||||||
.repaint_after
|
current_frame_nr: *self.viewports_frame_nr.entry(viewport_id).or_default(),
|
||||||
.get(&viewport_id)
|
viewport_id,
|
||||||
.copied()
|
};
|
||||||
.unwrap_or(std::time::Duration::MAX)
|
(callback)(info);
|
||||||
{
|
} else {
|
||||||
self.repaint_after.insert(viewport_id, after);
|
log::warn!("request_repaint_callback is not implemented by egui integration!\nIf is your integration you need to call `Context::set_request_repaint_callback`");
|
||||||
|
|
||||||
if let Some(callback) = &self.request_repaint_callback {
|
|
||||||
let info = RequestRepaintInfo {
|
|
||||||
after,
|
|
||||||
current_frame_nr: *self.viewports_frame_nr.entry(viewport_id).or_default(),
|
|
||||||
viewport_id,
|
|
||||||
};
|
|
||||||
(callback)(info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_frame(&mut self, viewport_id: ViewportId) {
|
fn start_frame(&mut self, viewport_id: ViewportId) {
|
||||||
// We are repainting; no need to reschedule a repaint unless the user asks for it again.
|
let request = self.repaint_request.entry(viewport_id).or_default();
|
||||||
self.repaint_after.remove(&viewport_id);
|
self.requested_repaint_last_frame
|
||||||
|
.insert(viewport_id, *request);
|
||||||
|
*request = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns how long to wait until repaint
|
// returns what is needed to be repainted
|
||||||
fn end_frame(
|
fn end_frame(&mut self, viewport_id: ViewportId, viewports: &[ViewportId]) {
|
||||||
&mut self,
|
|
||||||
viewport_id: ViewportId,
|
|
||||||
viewports: &[ViewportId],
|
|
||||||
) -> HashMap<ViewportId, std::time::Duration> {
|
|
||||||
// if repaint_requests is greater than zero. just set the duration to zero for immediate
|
|
||||||
// repaint. if there's no repaint requests, then we can use the actual repaint_after instead.
|
|
||||||
let repaint_after = if self
|
|
||||||
.repaint_requests
|
|
||||||
.get(&viewport_id)
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(0)
|
|
||||||
> 0
|
|
||||||
{
|
|
||||||
if let Some(requests) = self.repaint_requests.get_mut(&viewport_id) {
|
|
||||||
*requests -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::time::Duration::ZERO
|
|
||||||
} else {
|
|
||||||
self.repaint_after
|
|
||||||
.get(&viewport_id)
|
|
||||||
.copied()
|
|
||||||
.unwrap_or(std::time::Duration::MAX)
|
|
||||||
};
|
|
||||||
self.repaint_after.insert(viewport_id, repaint_after);
|
|
||||||
|
|
||||||
self.requested_repaint_last_frame = repaint_after.is_zero();
|
|
||||||
*self.viewports_frame_nr.entry(viewport_id).or_default() += 1;
|
*self.viewports_frame_nr.entry(viewport_id).or_default() += 1;
|
||||||
|
|
||||||
self.repaint_after.retain(|id, _| viewports.contains(id));
|
self.requested_repaint_last_frame
|
||||||
|
.retain(|id, _| viewports.contains(id));
|
||||||
self.viewports_frame_nr
|
self.viewports_frame_nr
|
||||||
.retain(|id, _| viewports.contains(id));
|
.retain(|id, _| viewports.contains(id));
|
||||||
self.repaint_requests
|
self.repaint_request.retain(|id, _| viewports.contains(id));
|
||||||
.retain(|id, repaints| viewports.contains(id) && *repaints != 0);
|
}
|
||||||
|
|
||||||
self.repaint_after.clone()
|
fn requested_repaint_last_frame(&self, viewport_id: &ViewportId) -> bool {
|
||||||
|
self.requested_repaint_last_frame
|
||||||
|
.get(viewport_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requested_repaint(&self, viewport_id: &ViewportId) -> bool {
|
||||||
|
self.repaint_request
|
||||||
|
.get(viewport_id)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,11 +236,10 @@ impl ContextImpl {
|
|||||||
pair.this,
|
pair.this,
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = self
|
let input = self.input.remove(&pair).unwrap_or_default().begin_frame(
|
||||||
.input
|
new_raw_input,
|
||||||
.remove(&pair)
|
self.repaint.requested_repaint_last_frame(&pair),
|
||||||
.unwrap_or_default()
|
);
|
||||||
.begin_frame(new_raw_input, self.repaint.requested_repaint_last_frame);
|
|
||||||
self.input.insert(pair.this, input);
|
self.input.insert(pair.this, input);
|
||||||
|
|
||||||
self.frame_state
|
self.frame_state
|
||||||
@@ -1279,6 +1231,26 @@ impl Context {
|
|||||||
self.write(|ctx| ctx.repaint.request_repaint_after(duration, id));
|
self.write(|ctx| ctx.repaint.request_repaint_after(duration, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// With this you can know if the application stal before
|
||||||
|
pub fn requested_repaint_last_frame(&self) -> bool {
|
||||||
|
self.requested_repaint_last_frame_for(&self.viewport_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With this you can know if the viewport stal before
|
||||||
|
pub fn requested_repaint_last_frame_for(&self, viewport_id: &ViewportId) -> bool {
|
||||||
|
self.read(|ctx| ctx.repaint.requested_repaint_last_frame(viewport_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With this you will know if the application will redraw
|
||||||
|
pub fn requested_repaint(&self) -> bool {
|
||||||
|
self.requested_repaint_for(&self.viewport_id())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With this you will know if the viewport will redraw
|
||||||
|
pub fn requested_repaint_for(&self, viewport_id: &ViewportId) -> bool {
|
||||||
|
self.read(|ctx| ctx.repaint.requested_repaint(viewport_id))
|
||||||
|
}
|
||||||
|
|
||||||
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
|
/// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`].
|
||||||
///
|
///
|
||||||
/// This lets you wake up a sleeping UI thread.
|
/// This lets you wake up a sleeping UI thread.
|
||||||
@@ -1665,12 +1637,10 @@ impl Context {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let repaint_after =
|
self.write(|ctx| ctx.repaint.end_frame(viewport_id, &avalibile_viewports));
|
||||||
self.write(|ctx| ctx.repaint.end_frame(viewport_id, &avalibile_viewports));
|
|
||||||
|
|
||||||
FullOutput {
|
FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
viewports,
|
viewports,
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
//! All the data egui returns to the backend at the end of each frame.
|
//! All the data egui returns to the backend at the end of each frame.
|
||||||
|
|
||||||
use ahash::HashMap;
|
|
||||||
|
|
||||||
use crate::ViewportId;
|
use crate::ViewportId;
|
||||||
use crate::{ViewportCommand, ViewportOutput, WidgetType};
|
use crate::{ViewportCommand, ViewportOutput, WidgetType};
|
||||||
|
|
||||||
@@ -13,16 +11,6 @@ pub struct FullOutput {
|
|||||||
/// Non-rendering related output.
|
/// Non-rendering related output.
|
||||||
pub platform_output: PlatformOutput,
|
pub platform_output: PlatformOutput,
|
||||||
|
|
||||||
/// If `Duration::is_zero()`, egui is requesting immediate repaint (i.e. on the next frame).
|
|
||||||
///
|
|
||||||
/// This happens for instance when there is an animation, or if a user has called `Context::request_repaint()`.
|
|
||||||
///
|
|
||||||
/// If `Duration` is greater than zero, egui wants to be repainted at or before the specified
|
|
||||||
/// duration elapses. when in reactive mode, egui spends forever waiting for input and only then,
|
|
||||||
/// will it repaint itself. this can be used to make sure that backend will only wait for a
|
|
||||||
/// specified amount of time, and repaint egui without any new input.
|
|
||||||
pub repaint_after: HashMap<ViewportId, std::time::Duration>,
|
|
||||||
|
|
||||||
/// Texture changes since last frame (including the font texture).
|
/// Texture changes since last frame (including the font texture).
|
||||||
///
|
///
|
||||||
/// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,
|
/// The backend needs to apply [`crate::TexturesDelta::set`] _before_ painting,
|
||||||
@@ -44,7 +32,6 @@ impl FullOutput {
|
|||||||
pub fn append(&mut self, newer: Self) {
|
pub fn append(&mut self, newer: Self) {
|
||||||
let Self {
|
let Self {
|
||||||
platform_output,
|
platform_output,
|
||||||
repaint_after,
|
|
||||||
textures_delta,
|
textures_delta,
|
||||||
shapes,
|
shapes,
|
||||||
mut viewports,
|
mut viewports,
|
||||||
@@ -52,7 +39,6 @@ impl FullOutput {
|
|||||||
} = newer;
|
} = newer;
|
||||||
|
|
||||||
self.platform_output.append(platform_output);
|
self.platform_output.append(platform_output);
|
||||||
self.repaint_after = repaint_after; // if the last frame doesn't need a repaint, then we don't need to repaint
|
|
||||||
self.textures_delta.append(textures_delta);
|
self.textures_delta.append(textures_delta);
|
||||||
self.shapes = shapes; // Only paint the latest
|
self.shapes = shapes; // Only paint the latest
|
||||||
self.viewports.append(&mut viewports);
|
self.viewports.append(&mut viewports);
|
||||||
|
|||||||
Reference in New Issue
Block a user