When I open two windows and close one of them, the other window causes a segfault during Pixels::render_with. This happened as a result of code restructurings that were needed to upgrade pixels from 0.15 to 0.17.
Steps to repro, using my reznez program, which uses pixels among other GUI libraries:
1. git pull https://github.com/merehap/reznez/tree/waylandbug
2. cargo run --release (this opens the primary window)
3. Open the reznez Status window by browsing the menu bar: "Debug Windows" >> "Status"
4. Close the Status window by pressing its "X".
Actual result: A segfault occurs. Not on the window close, but when the primary window attempts to render again, even though no change occurred to primary window
Expected result: The Status window closes, no segfault occurs.
Notes:
- reznez is using pixels, egui, winit, and wgpu together.
- Pixels isn't the only thing drawing to each Window. egui_winit is used to draw text and form elements to the same Windows.
- In the repro steps above, nothing is actually being written to the Pixels buffers (since no game ROM has been loaded), they are only being created and destroyed.
- The primary window has special logic that closes all windows when it is closed. This occurs before the segfault can take place. If this logic is removed, then closing the primary window causes the Status window to segfault.
- The segfault occurs even if there are more than 2 windows open when a window is closed.
- I'm delaying window close until RedrawRequested in the linked code as part of the debugging process. Since I'm not delaying it in the master branch, but I'm still getting the segfault there, I'm pretty sure that delay is not causing the problem.
Diff showing the relevant file with the debug logging statements added: merehap/reznez@39ec7e9
Relevant struct (two irrelevant fields omitted). I create one of these per actual GUI window. There is no shared state between different EguiWindows, but pixels, window, and wgpu_renderer within an EguiWindow do share state. This debug drop implementation never fails, the failure is during a later render_with call.
struct EguiWindow<'a> {
egui_state: egui_winit::State,
screen_descriptor: ScreenDescriptor,
paint_jobs: Vec<ClippedPrimitive>,
textures: TexturesDelta,
// Fields that share a Window using an Arc, or share other GPU resources. The ManuallyDrop wrappers are only present for debugging.
pixels: ManuallyDrop<Pixels<'a>>,
window: ManuallyDrop<Arc<Window>>,
wgpu_renderer: ManuallyDrop<Renderer>,
}
impl<'a> Drop for EguiWindow<'a> {
fn drop(&mut self) {
unsafe {
eprintln!("MANUAL DROP: renderer");
ManuallyDrop::drop(&mut self.wgpu_renderer);
eprintln!("MANUAL DROP: pixels");
ManuallyDrop::drop(&mut self.pixels);
eprintln!("MANUAL DROP: window");
ManuallyDrop::drop(&mut self.window);
eprintln!("MANUAL DROP: done");
}
}
}
Debug output demonstrating that this isn't just a drop order problem that is solvable in my code:
MANUAL DROP: renderer
MANUAL DROP: pixels
MANUAL DROP: window
MANUAL DROP: done
after close_pending WindowId(100476948890624)
before render_with
Segmentation fault (core dumped)
after render_with is never reached.
GDB stack trace
Thread 1 "reznez" received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0 0x0000000000000000 in ?? ()
#1 0x00007fffe692c423 in ?? () from /lib/x86_64-linux-gnu/libnvidia-glcore.so.580.126.18
#2 0x00007fffe6bb9a60 in ?? () from /lib/x86_64-linux-gnu/libnvidia-glcore.so.580.126.18
#3 0x0000555557a9fa61 in ash::extensions_generated::khr::swapchain::Device::acquire_next_image (self=0x55555a2501d0, swapchain=..., timeout=1000000000, semaphore=..., fence=...)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/ash-0.38.0+1.3.281/src/extensions/khr/swapchain.rs:62
#4 0x0000555557a6e20d in wgpu_hal::vulkan::swapchain::native::{impl#2}::acquire (self=0x55555a250130, timeout=..., fence=0x555559e7abc0) at src/vulkan/swapchain/native.rs:432
#5 0x0000555557949b05 in wgpu_hal::vulkan::instance::{impl#6}::acquire_texture (self=0x555559c2c510, timeout=..., fence=0x555559e7abc0) at src/vulkan/instance.rs:1031
#6 0x0000555557663238 in wgpu_hal::dynamic::surface::{impl#0}::acquire_texture<wgpu_hal::vulkan::Surface> (self=0x555559c2c510, timeout=..., fence=...)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/wgpu-hal-29.0.3/src/dynamic/surface.rs:59
#7 0x0000555557898da7 in wgpu_core::instance::Surface::get_current_texture (self=0x555559bc2750) at src/present.rs:169
#8 0x000055555789b4f6 in wgpu_core::global::Global::surface_get_current_texture (self=0x555559e6f2b0, surface_id=..., texture_id_in=...) at src/present.rs:373
#9 0x00005555575b1505 in wgpu::backend::wgpu_core::{impl#57}::get_current_texture (self=0x555559e70560) at src/backend/wgpu_core.rs:3906
#10 0x00005555575dbc39 in wgpu::api::surface::Surface::get_current_texture (self=0x55555a5d0390) at src/api/surface.rs:121
#11 0x00005555568c2770 in pixels::Pixels::render_with<reznez::gui::egui_gui::{impl#7}::draw::{closure_env#2}> (self=0x55555a5d0240, render_function=...)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pixels-0.17.1/src/lib.rs:556
#12 0x0000555556a19c4f in reznez::gui::egui_gui::EguiWindow::draw (self=0x55555a5cff28, world=0x5555597383a0) at src/gui/egui_gui.rs:348
#13 0x0000555556a1a743 in reznez::gui::egui_gui::WindowManager::draw (self=0x55555973a498, world=0x5555597383a0, window_id=...) at src/gui/egui_gui.rs:494
#14 0x0000555556a1bab0 in reznez::gui::egui_gui::{impl#2}::window_event (self=0x5555597383a0, event_loop=0x5555597455c0, window_id=..., event=...) at src/gui/egui_gui.rs:156
#15 0x0000555556a80681 in winit::event_loop::dispatch_event_for_app<(), reznez::gui::egui_gui::EguiGui> (app=0x5555597383a0, event_loop=0x5555597455c0, event=<optimized out>)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/event_loop.rs:642
#16 winit::event_loop::{impl#6}::run_app::{closure#0}<(), reznez::gui::egui_gui::EguiGui> (event=..., event_loop=0x5555597455c0)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/event_loop.rs:265
#17 0x0000555556a814bf in core::ops::function::impls::{impl#3}::call_mut<(winit::event::Event<()>, &winit::event_loop::ActiveEventLoop), winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (self=0x7fffffff79b0, args=...) at /home/sean/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:298
#18 0x0000555556a8151f in core::ops::function::impls::{impl#3}::call_mut<(winit::event::Event<()>, &winit::event_loop::ActiveEventLoop), &mut winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (self=0x7fffffff77a8, args=...) at /home/sean/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:298
#19 0x00005555568ef516 in winit::platform_impl::linux::wayland::event_loop::EventLoop<()>::single_iteration<(), &mut &mut winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (
self=0x555559745510, callback=0x7fffffff77a8, cause=...) at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs:502
#20 0x00005555568f0356 in winit::platform_impl::linux::wayland::event_loop::EventLoop<()>::poll_events_with_timeout<(), &mut &mut winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (
self=0x555559745510, timeout=..., callback=0x7fffffff79b0) at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs:328
#21 0x00005555568ecb0f in winit::platform_impl::linux::wayland::event_loop::EventLoop<()>::pump_events<(), &mut winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (
self=0x555559745510, timeout=..., callback=0x7fffffff7c38) at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs:226
#22 0x00005555568ed1b7 in winit::platform_impl::linux::wayland::event_loop::EventLoop<()>::run_on_demand<(), winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (self=0x555559745510,
event_handler=...) at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/wayland/event_loop/mod.rs:190
#23 0x00005555569eeee5 in winit::platform_impl::linux::EventLoop<()>::run_on_demand<(), winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (self=0x7fffffff7df0, callback=...)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/mod.rs:820
#24 0x00005555569eef1a in winit::platform_impl::linux::EventLoop<()>::run<(), winit::event_loop::{impl#6}::run_app::{closure_env#0}<(), reznez::gui::egui_gui::EguiGui>> (self=..., callback=...)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/platform_impl/linux/mod.rs:813
#25 0x0000555556a80541 in winit::event_loop::EventLoop<()>::run_app<(), reznez::gui::egui_gui::EguiGui> (self=..., app=0x5555597383a0)
at /home/sean/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/winit-0.30.13/src/event_loop.rs:265
#26 0x0000555556a1c203 in reznez::gui::egui_gui::{impl#1}::run (self=0x5555597383a0, nes=...) at src/gui/egui_gui.rs:110
#27 0x00005555569e79f2 in reznez::main () at src/main.rs:79
Pixels::render_with calls wgpu::api::surface::Surface::get_current_texture calls Vulcan calls libnvidia which seems to crash on a null pointer.
Relevant Cargo.toml entries:
egui = "0.34.1"
egui-wgpu = "0.34.1"
egui_extras = { version = "0.34.1", features = ["svg"] }
egui-winit = { version = "0.34.1", default-features = false, features = ["links"] }
egui_plot = "0.35.0"
egui_file = "0.26.0"
pixels = "0.17.1"
winit = "0.30.13"
winit_input_helper = "0.17"
I'm running Wayland running on PopOS 24.04 (Linux). @xNomadx1 confirmed this does NOT repro on X11 nor Mac.
I'll try to come up with a more minimal repro case for you as I assume that would be helpful. Let me know if you need any other info from me.
When I open two windows and close one of them, the other window causes a segfault during Pixels::render_with. This happened as a result of code restructurings that were needed to upgrade pixels from 0.15 to 0.17.
Steps to repro, using my reznez program, which uses pixels among other GUI libraries:
Actual result: A segfault occurs. Not on the window close, but when the primary window attempts to render again, even though no change occurred to primary window
Expected result: The Status window closes, no segfault occurs.
Notes:
Diff showing the relevant file with the debug logging statements added: merehap/reznez@39ec7e9
Relevant struct (two irrelevant fields omitted). I create one of these per actual GUI window. There is no shared state between different EguiWindows, but pixels, window, and wgpu_renderer within an EguiWindow do share state. This debug drop implementation never fails, the failure is during a later render_with call.
Debug output demonstrating that this isn't just a drop order problem that is solvable in my code:
after render_withis never reached.GDB stack trace
Pixels::render_with calls wgpu::api::surface::Surface::get_current_texture calls Vulcan calls libnvidia which seems to crash on a null pointer.
Relevant Cargo.toml entries:
I'm running Wayland running on PopOS 24.04 (Linux). @xNomadx1 confirmed this does NOT repro on X11 nor Mac.
I'll try to come up with a more minimal repro case for you as I assume that would be helpful. Let me know if you need any other info from me.