Repository: gfx-rs/wgpu (language: rust)

Issue:
[wayland] crash in eglTerminate on program exit
A test program that uses many wgpu features, but otherwise is fairly simple, segfaults on exit when run with winit 0.29. I don't know if this is a winit problem or a wgpu problem but wgpu_hal is in the gdb backtrace. I'm filing here first.

The GDB backtrace looks like

```
(gdb) bt

#0  0x00007ffff7fb1845 in ?? () from /lib/x86_64-linux-gnu/libwayland-client.so.0
#1  0x00007ffff7fb19a3 in ?? () from /lib/x86_64-linux-gnu/libwayland-client.so.0
#2  0x00007ffff7fb34d2 in wl_proxy_marshal_array_flags () from /lib/x86_64-linux-gnu/libwayland-client.so.0
#3  0x00007ffff7fb3f59 in wl_proxy_marshal_flags () from /lib/x86_64-linux-gnu/libwayland-client.so.0
#4  0x00007ffff46d9937 in ?? () from /lib/x86_64-linux-gnu/libEGL_mesa.so.0
#5  0x00007ffff46ce988 in ?? () from /lib/x86_64-linux-gnu/libEGL_mesa.so.0
#6  0x00007ffff46cee80 in ?? () from /lib/x86_64-linux-gnu/libEGL_mesa.so.0
#7  0x00007ffff46bc97c in ?? () from /lib/x86_64-linux-gnu/libEGL_mesa.so.0
#8  0x0000555556b7d8e2 in khronos_egl::{impl#98}::eglTerminate<libloading::safe::Library> (self=0x5555575d2cb0, 
    display=0x5555575dfc80)
    at /home/mcc/.cargo/registry/src/index.crates.io-6f17d22bba15001f/khronos-egl-6.0.0/src/lib.rs:2321
#9  khronos_egl::Instance<khronos_egl::Dynamic<libloading::safe::Library, khronos_egl::EGL1_4>>::terminate<khronos_egl::Dynamic<libloading::safe::Library, khronos_egl::EGL1_4>> (self=0x5555575d2cb0, display=...)
    at /home/mcc/.cargo/registry/src/index.crates.io-6f17d22bba15001f/khronos-egl-6.0.0/src/lib.rs:1181
#10 0x0000555556bfbbd4 in wgpu_hal::gles::egl::{impl#10}::drop (self=0x5555575dd9d8) at src/gles/egl.rs:621
#11 0x0000555556bc3177 in core::ptr::drop_in_place<wgpu_hal::gles::egl::Inner> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#12 0x0000555556acb27b in core::ptr::drop_in_place<core::cell::UnsafeCell<wgpu_hal::gles::egl::Inner>> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#13 0x0000555556ac893f in core::ptr::drop_in_place<lock_api::mutex::Mutex<parking_lot::raw_mutex::RawMutex, wgpu_hal::gles::egl::Inner>> () at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#14 0x0000555556ac9e0c in core::ptr::drop_in_place<wgpu_hal::gles::egl::Instance> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
--Type <RET> for more, q to quit, c to continue without paging--c

#15 0x0000555556acb3f3 in core::ptr::drop_in_place<core::option::Option<wgpu_hal::gles::egl::Instance>> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#16 0x000055555696e436 in core::ptr::drop_in_place<wgpu_core::instance::Instance> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#17 0x0000555556978a17 in core::ptr::drop_in_place<wgpu_core::global::Global<wgpu_core::identity::IdentityManagerFactory>> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#18 0x000055555696e634 in core::ptr::drop_in_place<wgpu::backend::direct::Context> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#19 0x000055555696ec70 in core::ptr::drop_in_place<dyn wgpu::context::DynContext> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#20 0x000055555686ea1a in alloc::sync::Arc<dyn wgpu::context::DynContext, alloc::alloc::Global>::drop_slow<dyn wgpu::context::DynContext, alloc::alloc::Global> (self=0x7ffffffeef10)
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/alloc/src/sync.rs:1749
#21 0x00005555568700d3 in alloc::sync::{impl#33}::drop<dyn wgpu::context::DynContext, alloc::alloc::Global> (
    self=0x7ffffffeef10) at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/alloc/src/sync.rs:2405
#22 0x0000555556972c8b in core::ptr::drop_in_place<alloc::sync::Arc<dyn wgpu::context::DynContext, alloc::alloc::Global>> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#23 0x0000555555a8ea47 in core::ptr::drop_in_place<wgpu::PipelineLayout> ()
    at /rustc/cc66ad468955717ab92600c770da8c1601a4ff33/library/core/src/ptr/mod.rs:497
#24 0x0000555555b030a3 in wgpu_hello::run::{async_fn#0} () at src/main.rs:742
#25 0x0000555555a859e3 in pollster::block_on<wgpu_hello::run::{async_fn_env#0}> (fut=...)
    at /home/mcc/.cargo/registry/src/index.crates.io-6f17d22bba15001f/pollster-0.3.0/src/lib.rs:128
#26 0x0000555555afc6fb in wgpu_hello::main () at src/main.rs:758
```

### DETAILS/REPRO

I have a project https://github.com/mcclure/webgpu-tutorial-rs . As the name suggests, it is a (mostly finished) WebGPU tutorial application. It is based on the hello-triangle example.

As of a few days ago, commit [a8e1516](https://github.com/mcclure/webgpu-tutorial-rs/commit/a8e1516a27474275fd6d4364b7d7cf09f79d67f4) was running wgpu 0.16 and winit 0.29-beta (a very early 0.29 beta, so that it had still the 0.28 API surface). This week, I attempted to upgrade. In commit [50c079e](https://github.com/mcclure/webgpu-tutorial-rs/commit/50c079e46f94d22a656cc1b8dc30ee616b1c2a75) I upgraded to winit 0.29 final (while keeping wgpu fixed at 0.16). This had the effect of making the application segfault at close time. Because 0.16 is old, I upgraded to wgpu 0.18. This lead to a different problem #4630, fixed in PR #4635. Because I am trying to test with 0.18 I backported to 0.18 in PR #4649 which you can find the example code for in branch [webgpu-tutorial-0.18-fork](https://github.com/mcclure/webgpu-tutorial-rs/tree/webgpu-tutorial-0.18-fork). You can test by checking out the webgpu-tutorial-0.18-fork branch and running cargo run (or commit 50c079e for the 0.16 version). **Warning:** When run successfully, this example has audio which currently may be somewhat loud.

Notice I select rwh_05 in Cargo.toml as directed by the wgpu matrix channel.

### Platform

I am running with Rust 1.73.0 on Ubuntu 23.10 with Wayland and an almost entirely stock desktop environment. I am on a "Lenovo ThinkPad T14 Gen 3" with gpu "AMD Ryzen™ 5 PRO 6650U with Radeon™ Graphics × 12". The failure occurs 100% of the time. I have not tested on other platforms.

Both the 0.16 and 0.18 wgpu branch implicated wgpu_hal in the bt, though I don't think they were the same bt.

### Expected behavior

In Rust, even if I do something wrong, I should get a panic with a clear error message rather than a segfault.

### Note

I would be happy to file this bug on winit instead (since it was upgrading winit, not upgrading wgpu, that caused the problem) but I need to be able to plausibly say "I asked wgpu and they said it's not their fault (even though their frames are all over the gdb stack)".


Discussion / hints:
I've also observed these for a while. They can be avoided by requesting an instance with `Backends::PRIMARY` instead of the default `Backends::all()`, since they are caused by the GL backend.
I tested and this problem is Wayland exclusive. If I log out, log back in to "Ubuntu with xorg", and repeat the tests, I do not get the crash in either 0.16 or 0.18.
> They can be avoided by requesting an instance with Backends::PRIMARY instead of the default Backends::all(), since they are caused by the GL backend.

Since not all systems have Vulkan, surely you want to include support for the GL backend?
> Since not all systems have Vulkan, surely you want to include support for the GL backend?

Yeah, YMMV. All systems I care about do support Vulkan, and I believe the GL backend has some other bugs too, so turning it off made sense.
Issue still happens in 0.20:
* https://github.com/gfx-rs/wgpu/issues/5655

Didn't happen in 0.19 because of another bug :/
FYI Still a thing in 23.0.1. Thankfully quite easy to [workaround by changing the drop order](https://github.com/PolyMeilex/Neothesia/pull/226) of `wgpu::Surface`.

```
Thread 1 "neothesia" received signal SIGSEGV, Segmentation fault.
0x00007ffff7fac08c in wl_map_insert_at (map=map@entry=0x55555823c930, flags=flags@entry=1, i=39, data=<optimized out>) at ../src/wayland-util.c:290
290		start[i].next |= (flags & 0x1) << 1;
(gdb) backtrace
#0  0x00007ffff7fac08c in wl_map_insert_at (map=map@entry=0x55555823c930, flags=flags@entry=1, i=39, data=<optimized out>) at ../src/wayland-util.c:290
#1  0x00007ffff7fac201 in proxy_destroy (proxy=proxy@entry=0x5555583a3990) at ../src/wayland-client.c:574
#2  0x00007ffff7fadf57 in wl_proxy_destroy_caller_locks (proxy=0x5555583a3990) at ../src/wayland-client.c:598
#3  wl_proxy_marshal_array_flags (proxy=proxy@entry=0x5555583a3990, opcode=opcode@entry=0, interface=interface@entry=0x0, version=version@entry=4, flags=flags@entry=1, args=args@entry=0x7fffffff8e90) at ../src/wayland-client.c:939
#4  0x00007ffff7faeb0a in wl_proxy_marshal_flags (proxy=proxy@entry=0x5555583a3990, opcode=opcode@entry=0, interface=interface@entry=0x0, version=4, flags=flags@entry=1) at ../src/wayland-client.c:857
#5  0x00007ffff4033a8e in wl_buffer_destroy (wl_buffer=0x5555583a3990) at /usr/include/wayland-client-protocol.h:2149
#6  dri2_teardown_wayland (dri2_dpy=dri2_dpy@entry=0x5555583a3ab0) at ../src/egl/drivers/dri2/platform_wayland.c:3043
#7  0x00007ffff4028158 in dri2_display_destroy (disp=disp@entry=0x5555583a2f00) at ../src/egl/drivers/dri2/egl_dri2.c:1153
#8  0x00007ffff40287e0 in dri2_display_release (disp=0x5555583a2f00) at ../src/egl/drivers/dri2/egl_dri2.c:1114
#9  dri2_display_release (disp=0x5555583a2f00) at ../src/egl/drivers/dri2/egl_dri2.c:1099
#10 dri2_terminate (disp=0x5555583a2f00) at ../src/egl/drivers/dri2/egl_dri2.c:1243
#11 0x00007ffff4014f74 in eglTerminate (dpy=<optimized out>) at ../src/egl/main/eglapi.c:774
#12 0x0000555557b71010 in khronos_egl::{impl#97}::eglTerminate<libloading::safe::Library> (self=0x55555830c880, display=0x5555583a2f00) at /home/poly/.cargo/registry/src/index.crates.io-6f17d22bba15001f/khronos-egl-6.0.0/src/lib.rs:2321
#13 khronos_egl::Instance<khronos_egl::Dynamic<libloading::safe::Library, khronos_egl::EGL1_4>>::terminate<khronos_egl::Dynamic<libloading::safe::Library, khronos_egl::EGL1_4>> (self=0x55555830c880, display=...)
    at /home/poly/.cargo/registry/src/index.crates.io-6f17d22bba15001f/khronos-egl-6.0.0/src/lib.rs:1181
#14 0x0000555557aad636 in wgpu_hal::gles::egl::terminate_display (egl=0x55555830c880, display=...) at src/gles/egl.rs:501
#15 0x0000555557ab1361 in wgpu_hal::gles::egl::{impl#11}::drop (self=0x5555583972b8) at src/gles/egl.rs:718
#16 0x0000555557b48a37 in core::ptr::drop_in_place<wgpu_hal::gles::egl::Inner> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#17 0x0000555557b4b04b in core::ptr::drop_in_place<core::cell::UnsafeCell<wgpu_hal::gles::egl::Inner>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#18 0x0000555557b45c0f in core::ptr::drop_in_place<lock_api::mutex::Mutex<parking_lot::raw_mutex::RawMutex, wgpu_hal::gles::egl::Inner>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#19 0x0000555557b48e7c in core::ptr::drop_in_place<wgpu_hal::gles::egl::Instance> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#20 0x00005555578f0204 in core::ptr::drop_in_place<alloc::boxed::Box<dyn wgpu_hal::dynamic::instance::DynInstance, alloc::alloc::Global>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#21 0x00005555578dee4f in core::ptr::drop_in_place<(wgpu_types::Backend, alloc::boxed::Box<dyn wgpu_hal::dynamic::instance::DynInstance, alloc::alloc::Global>)> ()
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#22 0x00005555578e075e in core::ptr::drop_in_place<[(wgpu_types::Backend, alloc::boxed::Box<dyn wgpu_hal::dynamic::instance::DynInstance, alloc::alloc::Global>)]> ()
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#23 0x0000555557766247 in alloc::vec::{impl#25}::drop<(wgpu_types::Backend, alloc::boxed::Box<dyn wgpu_hal::dynamic::instance::DynInstance, alloc::alloc::Global>), alloc::alloc::Global> (self=0x555558396018)
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/alloc/src/vec/mod.rs:3316
#24 0x00005555578e1b87 in core::ptr::drop_in_place<alloc::vec::Vec<(wgpu_types::Backend, alloc::boxed::Box<dyn wgpu_hal::dynamic::instance::DynInstance, alloc::alloc::Global>), alloc::alloc::Global>> ()
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#25 0x00005555578e57cc in core::ptr::drop_in_place<wgpu_core::instance::Instance> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#26 0x0000555557712d84 in core::ptr::drop_in_place<wgpu_core::global::Global> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#27 0x0000555557713c54 in core::ptr::drop_in_place<wgpu::backend::wgpu_core::ContextWgpuCore> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#28 0x000055555771337d in core::ptr::drop_in_place<dyn wgpu::context::DynContext> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#29 0x000055555773d8ba in alloc::sync::Arc<dyn wgpu::context::DynContext, alloc::alloc::Global>::drop_slow<dyn wgpu::context::DynContext, alloc::alloc::Global> (self=0x555558d5ca98)
    at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/alloc/src/sync.rs:1831
#30 0x000055555773df01 in alloc::sync::{impl#37}::drop<dyn wgpu::context::DynContext, alloc::alloc::Global> (self=0x555558d5ca98) at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/alloc/src/sync.rs:2524
#31 0x000055555771467b in core::ptr::drop_in_place<alloc::sync::Arc<dyn wgpu::context::DynContext, alloc::alloc::Global>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#32 0x0000555556bf5f17 in core::ptr::drop_in_place<wgpu::api::bind_group::BindGroup> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#33 0x0000555556b3a5ba in core::ptr::drop_in_place<wgpu_jumpstart::uniform::Uniform<neothesia_core::render::background_animation::TimeUniform>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#34 0x0000555556222ada in core::ptr::drop_in_place<neothesia_core::render::background_animation::BgPipeline> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#35 0x000055555622011a in core::ptr::drop_in_place<neothesia::scene::menu_scene::MenuScene> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#36 0x0000555556222844 in core::ptr::drop_in_place<alloc::boxed::Box<dyn neothesia::scene::Scene, alloc::alloc::Global>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#37 0x000055555621dcef in core::ptr::drop_in_place<neothesia::Neothesia> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#38 0x00005555562218f2 in core::ptr::drop_in_place<core::option::Option<neothesia::Neothesia>> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#39 0x000055555621ed77 in core::ptr::drop_in_place<neothesia::NeothesiaBootstrap> () at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ptr/mod.rs:574
#40 0x000055555625d1a3 in neothesia::main () at neothesia/src/main.rs:341
```
We had a user report this issue using wayland and a build of our game that uses WGPU v24.  We had to also change the drop order to drop the surface last to fix this issue.
So the issue here is that the _surface_ dies before the device, which makes wayland mad in some way. Maybe we need to represent this as both the surface and device hold some kind of common egl state which dies only when both do?
In my program `Surface<'static>` must be before `Arc<Window>` in my structure's drop order.
I suspect this is because the strong reference held by the surface is dropped before the surface itself if *fully* dropped.

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index acc67a82f5..e3ba0a617a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -78,6 +78,7 @@ By @brodycj in [#6925](https://github.com/gfx-rs/wgpu/pull/6925).
 - Add Flush to GL Queue::submit. By @cwfitzgerald in [#6941](https://github.com/gfx-rs/wgpu/pull/6941).
 - Fix `wgpu` not building with `--no-default-features` on when targeting `wasm32-unknown-unknown`. By @wumpf in [#6946](https://github.com/gfx-rs/wgpu/pull/6946).
 - Fix `CopyExternalImageDestInfo` not exported on `wgpu`. By @wumpf in [#6962](https://github.com/gfx-rs/wgpu/pull/6962).
+- Fix drop order in `Surface`. By @ed-2100 in [#6997](https://github.com/gfx-rs/wgpu/pull/6997)
 
 #### Vulkan
 
diff --git a/wgpu/src/api/surface.rs b/wgpu/src/api/surface.rs
index 37978dd43e..66bcdbd9c8 100644
--- a/wgpu/src/api/surface.rs
+++ b/wgpu/src/api/surface.rs
@@ -23,12 +23,6 @@ static_assertions::assert_impl_all!(SurfaceConfiguration: Send, Sync);
 /// [`GPUCanvasContext`](https://gpuweb.github.io/gpuweb/#canvas-context)
 /// serves a similar role.
 pub struct Surface<'window> {
-    /// Optionally, keep the source of the handle used for the surface alive.
-    ///
-    /// This is useful for platforms where the surface is created from a window and the surface
-    /// would become invalid when the window is dropped.
-    pub(crate) _handle_source: Option<Box<dyn WindowHandle + 'window>>,
-
     /// Additional surface data returned by [`DynContext::instance_create_surface`].
     pub(crate) inner: dispatch::DispatchSurface,
 
@@ -39,6 +33,14 @@ pub struct Surface<'window> {
     // be wrapped in a mutex and since the configuration is only supplied after the surface has
     // been created is is additionally wrapped in an option.
     pub(crate) config: Mutex<Option<SurfaceConfiguration>>,
+
+    /// Optionally, keep the source of the handle used for the surface alive.
+    ///
+    /// This is useful for platforms where the surface is created from a window and the surface
+    /// would become invalid when the window is dropped.
+    ///
+    /// SAFETY: This field must be dropped *after* all other fields to ensure proper cleanup.
+    pub(crate) _handle_source: Option<Box<dyn WindowHandle + 'window>>,
 }
 
 impl Surface<'_> {


Test patch: