1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 14:49:06 -04:00

Fix crash when parent viewport is hidden (#8226)

Hey,
this is my fist PR to fix the bug I just published #8225 
I hope it helps. 
I tested the fix with wgpu but not glow.
I must say that I am not very happy to have very similar function
is_viewport_or_descendant_visible in both case.
Let me know if you would prefer that I try to factorize it.

* Closes <https://github.com/emilk/egui/issues/8225>
* [X] I have followed the instructions in the PR template

---------

Co-authored-by: Matthieu Casanova <public@kpouer.com>
This commit is contained in:
Matthieu Casanova
2026-06-22 03:39:53 +02:00
committed by GitHub
parent 2c8c27c5df
commit 467c5b84f0
2 changed files with 53 additions and 11 deletions

View File

@@ -558,7 +558,7 @@ impl GlowWinitRunning<'_> {
}
}
let (raw_input, viewport_ui_cb, is_visible) = {
let (raw_input, viewport_ui_cb, is_visible, run_ui) = {
let mut glutin = self.glutin.borrow_mut();
let egui_ctx = glutin.egui_ctx.clone();
let Some(viewport) = glutin.viewports.get_mut(&viewport_id) else {
@@ -577,6 +577,9 @@ impl GlowWinitRunning<'_> {
let mut raw_input = egui_winit.take_egui_input(window);
let viewport_ui_cb = viewport.viewport_ui_cb.clone();
let run_ui =
is_visible || is_viewport_or_descendant_visible(&glutin.viewports, viewport_id);
self.integration.pre_update();
raw_input.time = Some(self.integration.beginning.elapsed().as_secs_f64());
@@ -586,7 +589,7 @@ impl GlowWinitRunning<'_> {
.map(|(id, viewport)| (*id, viewport.info.clone()))
.collect();
(raw_input, viewport_ui_cb, is_visible)
(raw_input, viewport_ui_cb, is_visible, run_ui)
};
// HACK: In order to get the right clear_color, the system theme needs to be set, which
@@ -641,7 +644,7 @@ impl GlowWinitRunning<'_> {
self.app.as_mut(),
viewport_ui_cb.as_deref(),
raw_input,
is_visible,
run_ui,
);
// ------------------------------------------------------------
@@ -1462,6 +1465,28 @@ fn initialize_or_update_viewport(
}
}
/// Is this viewport, or any of its (transitive) descendant viewports, visible?
///
/// Immediate viewports are rendered inline while their parent's UI runs, so even
/// if this viewport's window is occluded or minimized we must still run its UI to
/// give any visible descendant a chance to be painted.
fn is_viewport_or_descendant_visible(
viewports: &OrderedViewportIdMap<Viewport>,
viewport_id: ViewportId,
) -> bool {
let Some(viewport) = viewports.get(&viewport_id) else {
return false;
};
if viewport.info.visible().unwrap_or(true) {
return true;
}
viewports.values().any(|child| {
child.ids.parent == viewport_id
&& child.ids.this != viewport_id // ROOT is its own parent; avoid self-recursion.
&& is_viewport_or_descendant_visible(viewports, child.ids.this)
})
}
/// This is called (via a callback) by user code to render immediate viewports,
/// i.e. viewport that are directly nested inside a parent viewport.
fn render_immediate_viewport(

View File

@@ -601,7 +601,7 @@ impl WgpuWinitRunning<'_> {
let mut frame_timer = crate::stopwatch::Stopwatch::new();
frame_timer.start();
let (viewport_ui_cb, raw_input, is_visible) = {
let (viewport_ui_cb, raw_input, is_visible, run_ui) = {
profiling::scope!("Prepare");
let mut shared_lock = shared.borrow_mut();
@@ -657,6 +657,8 @@ impl WgpuWinitRunning<'_> {
};
let mut raw_input = egui_winit.take_egui_input(window);
let run_ui = is_visible || is_viewport_or_descendant_visible(viewports, viewport_id);
integration.pre_update();
raw_input.time = Some(integration.beginning.elapsed().as_secs_f64());
@@ -667,19 +669,15 @@ impl WgpuWinitRunning<'_> {
painter.handle_screenshots(&mut raw_input.events);
(viewport_ui_cb, raw_input, is_visible)
(viewport_ui_cb, raw_input, is_visible, run_ui)
};
// ------------------------------------------------------------
// Runs the update, which could call immediate viewports,
// so make sure we hold no locks here!
let full_output = integration.update(
app.as_mut(),
viewport_ui_cb.as_deref(),
raw_input,
is_visible,
);
let full_output =
integration.update(app.as_mut(), viewport_ui_cb.as_deref(), raw_input, run_ui);
// ------------------------------------------------------------
@@ -1026,6 +1024,25 @@ fn create_window(
Ok((window, viewport_builder))
}
/// Is this viewport, or any of its (transitive) descendant viewports, visible?
///
/// Immediate viewports are rendered inline while their parent's UI runs, so even
/// if this viewport's window is occluded or minimized we must still run its UI to
/// give any visible descendant a chance to be painted.
fn is_viewport_or_descendant_visible(viewports: &Viewports, viewport_id: ViewportId) -> bool {
let Some(viewport) = viewports.get(&viewport_id) else {
return false;
};
if viewport.info.visible().unwrap_or(true) {
return true;
}
viewports.values().any(|child| {
child.ids.parent == viewport_id
&& child.ids.this != viewport_id
&& is_viewport_or_descendant_visible(viewports, child.ids.this)
})
}
fn render_immediate_viewport(
beginning: Instant,
shared: &RefCell<SharedState>,