From 12c42fd837182816ce09eb1652db0145ed121501 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Sat, 19 Jul 2025 18:08:10 +0200 Subject: [PATCH] gles/egl: Require surface before creating instance / query adapter on Wayland --- examples/features/src/framework.rs | 22 ++++++++++++-------- examples/features/src/utils.rs | 33 +++++++++++++++++------------- wgpu-hal/src/gles/egl.rs | 30 +++++++++++++++++++++++++-- wgpu-types/src/lib.rs | 2 +- 4 files changed, 61 insertions(+), 26 deletions(-) diff --git a/examples/features/src/framework.rs b/examples/features/src/framework.rs index 7a3017848fa..fcb0b7ca812 100644 --- a/examples/features/src/framework.rs +++ b/examples/features/src/framework.rs @@ -141,10 +141,16 @@ impl SurfaceWrapper { /// On wasm, we need to create the surface here, as the WebGL backend needs /// a surface (and hence a canvas) to be present to create the adapter. /// + /// On Wayland our context creation (both inside the [`Instance`] and [`Adapter`]) requires + /// access to the `RawDisplayHandle`. Since this is currently not provided, a compatible + /// context is set up via the surface instead. + /// /// We cannot unconditionally create a surface here, as Android requires - /// us to wait until we receive the `Resumed` event to do so. + /// us to wait until we receive [`Event::Resumed`] to do so. fn pre_adapter(&mut self, instance: &Instance, window: Arc) { - if cfg!(target_arch = "wasm32") { + // XXX: Also needed for EGL+Wayland! + // if cfg!(target_arch = "wasm32") { + if !cfg!(target_os = "android") { self.surface = Some(instance.create_surface(window).unwrap()); } } @@ -153,6 +159,8 @@ impl SurfaceWrapper { fn start_condition(e: &Event<()>) -> bool { match e { // On all other platforms, we can create the surface immediately. + // XXX: winit was improved to consistently emit a Resumed event on all platforms, which + // is the right place to create surfaces... Most platforms emit it right after Init. Event::NewEvents(StartCause::Init) => !cfg!(target_os = "android"), // On android we need to wait for a resumed event to create the surface. Event::Resumed => cfg!(target_os = "android"), @@ -174,13 +182,9 @@ impl SurfaceWrapper { log::info!("Surface resume {window_size:?}"); // We didn't create the surface in pre_adapter, so we need to do so now. - if !cfg!(target_arch = "wasm32") { - self.surface = Some(context.instance.create_surface(window).unwrap()); - } - - // From here on, self.surface should be Some. - - let surface = self.surface.as_ref().unwrap(); + let surface = self + .surface + .get_or_insert_with(|| context.instance.create_surface(window).unwrap()); // Get the default configuration, let mut config = surface diff --git a/examples/features/src/utils.rs b/examples/features/src/utils.rs index 5d2913c185a..75dd904c95d 100644 --- a/examples/features/src/utils.rs +++ b/examples/features/src/utils.rs @@ -203,24 +203,29 @@ pub(crate) async fn get_adapter_with_capabilities_or_from_env( ); adapter } else { - let adapters = instance.enumerate_adapters(Backends::all()); - let mut chosen_adapter = None; - for adapter in adapters { - if let Some(surface) = surface { - if !adapter.is_surface_supported(surface) { + + if let Some(surface) = surface { + chosen_adapter = instance + .request_adapter(&wgpu::RequestAdapterOptionsBase { + compatible_surface: Some(surface), + ..Default::default() + }) + .await + .ok(); + } else { + let adapters = instance.enumerate_adapters(Backends::all()); + + for adapter in adapters { + let required_features = *required_features; + let adapter_features = adapter.features(); + if !adapter_features.contains(required_features) { continue; + } else { + chosen_adapter = Some(adapter); + break; } } - - let required_features = *required_features; - let adapter_features = adapter.features(); - if !adapter_features.contains(required_features) { - continue; - } else { - chosen_adapter = Some(adapter); - break; - } } chosen_adapter.expect("No suitable GPU adapters found on the system!") diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index ce1dd1299cf..675bc24773f 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -291,6 +291,12 @@ struct EglContext { impl EglContext { fn make_current(&self) { + log::trace!( + "Make current on {:?} {:?} {:?}", + self.display, + self.pbuffer, + self.raw + ); self.instance .make_current(self.display, self.pbuffer, self.pbuffer, Some(self.raw)) .unwrap(); @@ -697,6 +703,7 @@ impl Inner { version, supports_native_window, config, + // Will in some scenarios be overwritten by the caller after this function returns. wl_display: None, srgb_kind, force_gles_minor_version, @@ -839,7 +846,10 @@ impl crate::Instance for Instance { let (display, display_owner, wsi_kind) = if let (Some(library), Some(egl)) = (wayland_library, egl1_5) { - log::info!("Using Wayland platform"); + // XXX: Drop this code and convert `Inner` to an `Option` so that we can + // force-initialize this whenever the "RawDisplayHandle" is available (currently + // only via the surface)... + log::error!("Using Wayland platform, creating nonfunctional bogus display first"); let display_attributes = [khronos_egl::ATTRIB_NONE]; let display = unsafe { egl.get_platform_display( @@ -969,6 +979,7 @@ impl crate::Instance for Instance { ) .unwrap(); + // TODO: Why? let ret = unsafe { ndk_sys::ANativeWindow_setBuffersGeometry( handle @@ -1013,6 +1024,7 @@ impl crate::Instance for Instance { .unwrap() .get_platform_display( EGL_PLATFORM_WAYLAND_KHR, + // khronos_egl::DEFAULT_DISPLAY, display_handle.display.as_ptr(), &display_attributes, ) @@ -1056,9 +1068,22 @@ impl crate::Instance for Instance { unsafe fn enumerate_adapters( &self, - _surface_hint: Option<&Surface>, + surface_hint: Option<&Surface>, ) -> Vec> { let inner = self.inner.lock(); + + if let Some(surface) = surface_hint { + assert_eq!(surface.egl.raw, inner.egl.raw); + } else + // if inner.??? == Wayland + { + // This is a trick from Web, but it's too restrictive and not needed if the user + // initializes in the right order. + // TODO: We can probably also allow the user through if inner.wl_surface is not NULL? + log::info!("Returning zero adapters on Wayland unless a surface is passed"); + return vec![]; + } + inner.egl.make_current(); let mut gl = unsafe { @@ -1313,6 +1338,7 @@ impl crate::Surface for Surface { let wl_egl_window_create: libloading::Symbol = unsafe { library.get(c"wl_egl_window_create".to_bytes()) }.unwrap(); let window = + // XXX: Why hardcode a random size here, over using config.extent? unsafe { wl_egl_window_create(handle.surface.as_ptr(), 640, 480) } .cast(); wl_window = Some(window); diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index e05822736ad..16eacbeebb3 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -361,7 +361,7 @@ pub struct RequestAdapterOptions { pub force_fallback_adapter: bool, /// Surface that is required to be presentable with the requested adapter. This does not /// create the surface, only guarantees that the adapter can present to said surface. - /// For WebGL, this is strictly required, as an adapter can not be created without a surface. + /// For WebGL and Wayland, this is strictly required, as an adapter can not be created without a surface. pub compatible_surface: Option, }