mirror of
https://github.com/emilk/egui.git
synced 2026-08-31 22:00:03 -04:00
Only run App::ui if the application is visible (#7950)
* Closes https://github.com/emilk/egui/issues/5113 * Part of https://github.com/emilk/egui/issues/5112 * Part of https://github.com/emilk/egui/issues/5136 If the application is invisible (occluded or minimized), and the user calls `.request_repaint`, then we should call `App::logic`, but NOT `App::ui`. There are still some situations where `App::logic` is not called when it should be, but at least now we can skip running the UI code when the app is invisible.
This commit is contained in:
@@ -265,6 +265,7 @@ impl EpiIntegration {
|
|||||||
app: &mut dyn epi::App,
|
app: &mut dyn epi::App,
|
||||||
viewport_ui_cb: Option<&DeferredViewportUiCallback>,
|
viewport_ui_cb: Option<&DeferredViewportUiCallback>,
|
||||||
mut raw_input: egui::RawInput,
|
mut raw_input: egui::RawInput,
|
||||||
|
is_visible: bool,
|
||||||
) -> egui::FullOutput {
|
) -> egui::FullOutput {
|
||||||
raw_input.time = Some(self.beginning.elapsed().as_secs_f64());
|
raw_input.time = Some(self.beginning.elapsed().as_secs_f64());
|
||||||
|
|
||||||
@@ -275,23 +276,27 @@ impl EpiIntegration {
|
|||||||
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
||||||
if let Some(viewport_ui_cb) = viewport_ui_cb {
|
if let Some(viewport_ui_cb) = viewport_ui_cb {
|
||||||
// Child viewport
|
// Child viewport
|
||||||
profiling::scope!("viewport_callback");
|
if is_visible {
|
||||||
viewport_ui_cb(ui);
|
profiling::scope!("viewport_callback");
|
||||||
|
viewport_ui_cb(ui);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
{
|
{
|
||||||
profiling::scope!("App::logic");
|
profiling::scope!("App::logic");
|
||||||
app.logic(ui.ctx(), &mut self.frame);
|
app.logic(ui.ctx(), &mut self.frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if is_visible {
|
||||||
profiling::scope!("App::update");
|
{
|
||||||
#[expect(deprecated)]
|
profiling::scope!("App::update");
|
||||||
app.update(ui.ctx(), &mut self.frame);
|
#[expect(deprecated)]
|
||||||
}
|
app.update(ui.ctx(), &mut self.frame);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("App::ui");
|
profiling::scope!("App::ui");
|
||||||
app.ui(ui, &mut self.frame);
|
app.ui(ui, &mut self.frame);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -545,7 +545,7 @@ impl GlowWinitRunning<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (raw_input, viewport_ui_cb) = {
|
let (raw_input, viewport_ui_cb, is_visible) = {
|
||||||
let mut glutin = self.glutin.borrow_mut();
|
let mut glutin = self.glutin.borrow_mut();
|
||||||
let egui_ctx = glutin.egui_ctx.clone();
|
let egui_ctx = glutin.egui_ctx.clone();
|
||||||
let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {
|
let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {
|
||||||
@@ -556,6 +556,8 @@ impl GlowWinitRunning<'_> {
|
|||||||
};
|
};
|
||||||
egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window, false);
|
egui_winit::update_viewport_info(&mut viewport.info, &egui_ctx, window, false);
|
||||||
|
|
||||||
|
let is_visible = viewport.info.visible().unwrap_or(true);
|
||||||
|
|
||||||
let Some(egui_winit) = viewport.egui_winit.as_mut() else {
|
let Some(egui_winit) = viewport.egui_winit.as_mut() else {
|
||||||
return Ok(EventResult::Wait);
|
return Ok(EventResult::Wait);
|
||||||
};
|
};
|
||||||
@@ -571,7 +573,7 @@ impl GlowWinitRunning<'_> {
|
|||||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
(raw_input, viewport_ui_cb)
|
(raw_input, viewport_ui_cb, is_visible)
|
||||||
};
|
};
|
||||||
|
|
||||||
// HACK: In order to get the right clear_color, the system theme needs to be set, which
|
// HACK: In order to get the right clear_color, the system theme needs to be set, which
|
||||||
@@ -587,7 +589,7 @@ impl GlowWinitRunning<'_> {
|
|||||||
let has_many_viewports = self.glutin.borrow().viewports.len() > 1;
|
let has_many_viewports = self.glutin.borrow().viewports.len() > 1;
|
||||||
let clear_before_update = !has_many_viewports; // HACK: for some reason, an early clear doesn't "take" on Mac with multiple viewports.
|
let clear_before_update = !has_many_viewports; // HACK: for some reason, an early clear doesn't "take" on Mac with multiple viewports.
|
||||||
|
|
||||||
if clear_before_update {
|
if is_visible && clear_before_update {
|
||||||
// clear before we call update, so users can paint between clear-color and egui windows:
|
// clear before we call update, so users can paint between clear-color and egui windows:
|
||||||
|
|
||||||
let mut glutin = self.glutin.borrow_mut();
|
let mut glutin = self.glutin.borrow_mut();
|
||||||
@@ -622,9 +624,12 @@ impl GlowWinitRunning<'_> {
|
|||||||
// The update function, which could call immediate viewports,
|
// The update function, which could call immediate viewports,
|
||||||
// so make sure we don't hold any locks here required by the immediate viewports rendeer.
|
// so make sure we don't hold any locks here required by the immediate viewports rendeer.
|
||||||
|
|
||||||
let full_output =
|
let full_output = self.integration.update(
|
||||||
self.integration
|
self.app.as_mut(),
|
||||||
.update(self.app.as_mut(), viewport_ui_cb.as_deref(), raw_input);
|
viewport_ui_cb.as_deref(),
|
||||||
|
raw_input,
|
||||||
|
is_visible,
|
||||||
|
);
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
@@ -667,85 +672,87 @@ impl GlowWinitRunning<'_> {
|
|||||||
|
|
||||||
egui_winit.handle_platform_output(&window, platform_output);
|
egui_winit.handle_platform_output(&window, platform_output);
|
||||||
|
|
||||||
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
if is_visible {
|
||||||
|
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
||||||
|
|
||||||
{
|
{
|
||||||
// We may need to switch contexts again, because of immediate viewports:
|
// We may need to switch contexts again, because of immediate viewports:
|
||||||
frame_timer.pause();
|
frame_timer.pause();
|
||||||
change_gl_context(current_gl_context, not_current_gl_context, gl_surface);
|
change_gl_context(current_gl_context, not_current_gl_context, gl_surface);
|
||||||
frame_timer.resume();
|
frame_timer.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
|
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
|
||||||
|
|
||||||
if !clear_before_update {
|
if !clear_before_update {
|
||||||
painter.clear(screen_size_in_pixels, clear_color);
|
painter.clear(screen_size_in_pixels, clear_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.paint_and_update_textures(
|
painter.paint_and_update_textures(
|
||||||
screen_size_in_pixels,
|
screen_size_in_pixels,
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
&clipped_primitives,
|
&clipped_primitives,
|
||||||
&textures_delta,
|
&textures_delta,
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
for action in viewport.actions_requested.drain(..) {
|
for action in viewport.actions_requested.drain(..) {
|
||||||
match action {
|
match action {
|
||||||
ActionRequested::Screenshot(user_data) => {
|
ActionRequested::Screenshot(user_data) => {
|
||||||
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
||||||
egui_winit
|
egui_winit
|
||||||
.egui_input_mut()
|
.egui_input_mut()
|
||||||
.events
|
.events
|
||||||
.push(egui::Event::Screenshot {
|
.push(egui::Event::Screenshot {
|
||||||
viewport_id,
|
viewport_id,
|
||||||
user_data,
|
user_data,
|
||||||
image: screenshot.into(),
|
image: screenshot.into(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ActionRequested::Cut => {
|
ActionRequested::Cut => {
|
||||||
egui_winit.egui_input_mut().events.push(egui::Event::Cut);
|
egui_winit.egui_input_mut().events.push(egui::Event::Cut);
|
||||||
}
|
}
|
||||||
ActionRequested::Copy => {
|
ActionRequested::Copy => {
|
||||||
egui_winit.egui_input_mut().events.push(egui::Event::Copy);
|
egui_winit.egui_input_mut().events.push(egui::Event::Copy);
|
||||||
}
|
}
|
||||||
ActionRequested::Paste => {
|
ActionRequested::Paste => {
|
||||||
if let Some(contents) = egui_winit.clipboard_text() {
|
if let Some(contents) = egui_winit.clipboard_text() {
|
||||||
let contents = contents.replace("\r\n", "\n");
|
let contents = contents.replace("\r\n", "\n");
|
||||||
if !contents.is_empty() {
|
if !contents.is_empty() {
|
||||||
egui_winit
|
egui_winit
|
||||||
.egui_input_mut()
|
.egui_input_mut()
|
||||||
.events
|
.events
|
||||||
.push(egui::Event::Paste(contents));
|
.push(egui::Event::Paste(contents));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
integration.post_rendering(&window);
|
||||||
}
|
}
|
||||||
|
|
||||||
integration.post_rendering(&window);
|
{
|
||||||
}
|
// vsync - don't count as frame-time:
|
||||||
|
frame_timer.pause();
|
||||||
|
profiling::scope!("swap_buffers");
|
||||||
|
let context = current_gl_context.as_ref().ok_or_else(|| {
|
||||||
|
egui_glow::PainterError::from(
|
||||||
|
"failed to get current context to swap buffers".to_owned(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
{
|
gl_surface.swap_buffers(context)?;
|
||||||
// vsync - don't count as frame-time:
|
frame_timer.resume();
|
||||||
frame_timer.pause();
|
}
|
||||||
profiling::scope!("swap_buffers");
|
|
||||||
let context = current_gl_context.as_ref().ok_or_else(|| {
|
|
||||||
egui_glow::PainterError::from(
|
|
||||||
"failed to get current context to swap buffers".to_owned(),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
gl_surface.swap_buffers(context)?;
|
// give it time to settle:
|
||||||
frame_timer.resume();
|
#[cfg(feature = "__screenshot")]
|
||||||
}
|
if integration.egui_ctx.cumulative_pass_nr() == 2
|
||||||
|
&& let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO")
|
||||||
// give it time to settle:
|
{
|
||||||
#[cfg(feature = "__screenshot")]
|
save_screenshot_and_exit(&path, &painter, screen_size_in_pixels);
|
||||||
if integration.egui_ctx.cumulative_pass_nr() == 2
|
}
|
||||||
&& let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO")
|
|
||||||
{
|
|
||||||
save_screenshot_and_exit(&path, &painter, screen_size_in_pixels);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glutin.handle_viewport_output(event_loop, &integration.egui_ctx, &viewport_output);
|
glutin.handle_viewport_output(event_loop, &integration.egui_ctx, &viewport_output);
|
||||||
|
|||||||
@@ -573,7 +573,7 @@ impl WgpuWinitRunning<'_> {
|
|||||||
let mut frame_timer = crate::stopwatch::Stopwatch::new();
|
let mut frame_timer = crate::stopwatch::Stopwatch::new();
|
||||||
frame_timer.start();
|
frame_timer.start();
|
||||||
|
|
||||||
let (viewport_ui_cb, raw_input) = {
|
let (viewport_ui_cb, raw_input, is_visible) = {
|
||||||
profiling::scope!("Prepare");
|
profiling::scope!("Prepare");
|
||||||
let mut shared_lock = shared.borrow_mut();
|
let mut shared_lock = shared.borrow_mut();
|
||||||
|
|
||||||
@@ -617,6 +617,8 @@ impl WgpuWinitRunning<'_> {
|
|||||||
};
|
};
|
||||||
egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false);
|
egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false);
|
||||||
|
|
||||||
|
let is_visible = viewport.info.visible().unwrap_or(true);
|
||||||
|
|
||||||
{
|
{
|
||||||
profiling::scope!("set_window");
|
profiling::scope!("set_window");
|
||||||
pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?;
|
pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?;
|
||||||
@@ -637,14 +639,19 @@ impl WgpuWinitRunning<'_> {
|
|||||||
|
|
||||||
painter.handle_screenshots(&mut raw_input.events);
|
painter.handle_screenshots(&mut raw_input.events);
|
||||||
|
|
||||||
(viewport_ui_cb, raw_input)
|
(viewport_ui_cb, raw_input, is_visible)
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
// Runs the update, which could call immediate viewports,
|
// Runs the update, which could call immediate viewports,
|
||||||
// so make sure we hold no locks here!
|
// so make sure we hold no locks here!
|
||||||
let full_output = integration.update(app.as_mut(), viewport_ui_cb.as_deref(), raw_input);
|
let full_output = integration.update(
|
||||||
|
app.as_mut(),
|
||||||
|
viewport_ui_cb.as_deref(),
|
||||||
|
raw_input,
|
||||||
|
is_visible,
|
||||||
|
);
|
||||||
|
|
||||||
// ------------------------------------------------------------
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
@@ -685,52 +692,58 @@ impl WgpuWinitRunning<'_> {
|
|||||||
|
|
||||||
egui_winit.handle_platform_output(window, platform_output);
|
egui_winit.handle_platform_output(window, platform_output);
|
||||||
|
|
||||||
let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);
|
let vsync_secs = if is_visible {
|
||||||
|
let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);
|
||||||
|
|
||||||
let mut screenshot_commands = vec![];
|
let mut screenshot_commands = vec![];
|
||||||
viewport.actions_requested.retain(|cmd| {
|
viewport.actions_requested.retain(|cmd| {
|
||||||
if let ActionRequested::Screenshot(info) = cmd {
|
if let ActionRequested::Screenshot(info) = cmd {
|
||||||
screenshot_commands.push(info.clone());
|
screenshot_commands.push(info.clone());
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let vsync_secs = painter.paint_and_update_textures(
|
let vsync_secs = painter.paint_and_update_textures(
|
||||||
viewport_id,
|
viewport_id,
|
||||||
pixels_per_point,
|
pixels_per_point,
|
||||||
app.clear_color(&egui_ctx.global_style().visuals),
|
app.clear_color(&egui_ctx.global_style().visuals),
|
||||||
&clipped_primitives,
|
&clipped_primitives,
|
||||||
&textures_delta,
|
&textures_delta,
|
||||||
screenshot_commands,
|
screenshot_commands,
|
||||||
);
|
);
|
||||||
|
|
||||||
for action in viewport.actions_requested.drain(..) {
|
for action in viewport.actions_requested.drain(..) {
|
||||||
match action {
|
match action {
|
||||||
ActionRequested::Screenshot { .. } => {
|
ActionRequested::Screenshot { .. } => {
|
||||||
// already handled above
|
// already handled above
|
||||||
}
|
}
|
||||||
ActionRequested::Cut => {
|
ActionRequested::Cut => {
|
||||||
egui_winit.egui_input_mut().events.push(egui::Event::Cut);
|
egui_winit.egui_input_mut().events.push(egui::Event::Cut);
|
||||||
}
|
}
|
||||||
ActionRequested::Copy => {
|
ActionRequested::Copy => {
|
||||||
egui_winit.egui_input_mut().events.push(egui::Event::Copy);
|
egui_winit.egui_input_mut().events.push(egui::Event::Copy);
|
||||||
}
|
}
|
||||||
ActionRequested::Paste => {
|
ActionRequested::Paste => {
|
||||||
if let Some(contents) = egui_winit.clipboard_text() {
|
if let Some(contents) = egui_winit.clipboard_text() {
|
||||||
let contents = contents.replace("\r\n", "\n");
|
let contents = contents.replace("\r\n", "\n");
|
||||||
if !contents.is_empty() {
|
if !contents.is_empty() {
|
||||||
egui_winit
|
egui_winit
|
||||||
.egui_input_mut()
|
.egui_input_mut()
|
||||||
.events
|
.events
|
||||||
.push(egui::Event::Paste(contents));
|
.push(egui::Event::Paste(contents));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
integration.post_rendering(window);
|
integration.post_rendering(window);
|
||||||
|
|
||||||
|
vsync_secs
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
|
||||||
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
||||||
|
|
||||||
|
|||||||
@@ -274,13 +274,21 @@ impl AppRunner {
|
|||||||
|
|
||||||
self.app.raw_input_hook(&self.egui_ctx, &mut raw_input);
|
self.app.raw_input_hook(&self.egui_ctx, &mut raw_input);
|
||||||
|
|
||||||
|
let is_visible = raw_input
|
||||||
|
.viewports
|
||||||
|
.get(&egui::ViewportId::ROOT)
|
||||||
|
.and_then(|v| v.visible())
|
||||||
|
.unwrap_or(true);
|
||||||
|
|
||||||
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
let full_output = self.egui_ctx.run_ui(raw_input, |ui| {
|
||||||
self.app.logic(ui.ctx(), &mut self.frame);
|
self.app.logic(ui.ctx(), &mut self.frame);
|
||||||
|
|
||||||
#[expect(deprecated)]
|
if is_visible {
|
||||||
self.app.update(ui.ctx(), &mut self.frame);
|
#[expect(deprecated)]
|
||||||
|
self.app.update(ui.ctx(), &mut self.frame);
|
||||||
|
|
||||||
self.app.ui(ui, &mut self.frame);
|
self.app.ui(ui, &mut self.frame);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
let egui::FullOutput {
|
let egui::FullOutput {
|
||||||
platform_output,
|
platform_output,
|
||||||
@@ -311,8 +319,10 @@ impl AppRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.handle_platform_output(platform_output);
|
self.handle_platform_output(platform_output);
|
||||||
self.textures_delta.append(textures_delta);
|
if is_visible {
|
||||||
self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point));
|
self.textures_delta.append(textures_delta);
|
||||||
|
self.clipped_primitives = Some(self.egui_ctx.tessellate(shapes, pixels_per_point));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Paint the results of the last call to [`Self::logic`].
|
/// Paint the results of the last call to [`Self::logic`].
|
||||||
|
|||||||
Reference in New Issue
Block a user