diff --git a/CHANGELOG.md b/CHANGELOG.md index a1cb2650a6c..1d8e6b582bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,6 +91,10 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913). - Fixed a bug where access to matrices with 2 rows would not work in some cases. By @andyleiserson in [#7438](https://github.com/gfx-rs/wgpu/pull/7438). +##### EGL + +- Fixed unwrap failed in context creation for some Android devices. By @uael in [#8024](https://github.com/gfx-rs/wgpu/pull/8024). + ## v26.0.4 (2025-08-07) ### Bug Fixes diff --git a/wgpu-hal/src/gles/egl.rs b/wgpu-hal/src/gles/egl.rs index ff4d77a3357..43b817745d8 100644 --- a/wgpu-hal/src/gles/egl.rs +++ b/wgpu-hal/src/gles/egl.rs @@ -136,8 +136,13 @@ impl Drop for DisplayOwner { fn drop(&mut self) { match self.display { DisplayRef::X11(ptr) => unsafe { - let func: libloading::Symbol = - self.library.get(c"XCloseDisplay".to_bytes()).unwrap(); + let Ok(func): Result, _> = self + .library + .get(c"XCloseDisplay".to_bytes()) + .inspect_err(|err| log::error!("Failed to load XCloseDisplay: {err:?}")) + else { + return; + }; func(ptr.as_ptr()); }, DisplayRef::Wayland => {} @@ -149,8 +154,10 @@ fn open_x_display() -> Option { log::debug!("Loading X11 library to get the current display"); unsafe { let library = find_library(&["libX11.so.6", "libX11.so"])?; - let func: libloading::Symbol = - library.get(c"XOpenDisplay".to_bytes()).unwrap(); + let func: libloading::Symbol = library + .get(c"XOpenDisplay".to_bytes()) + .inspect_err(|err| log::error!("Failed to load XOpenDisplay: {err:?}")) + .ok()?; let result = func(ptr::null()); ptr::NonNull::new(result).map(|ptr| DisplayOwner { display: DisplayRef::X11(ptr), @@ -178,10 +185,12 @@ fn test_wayland_display() -> Option { let client_library = find_library(&["libwayland-client.so.0", "libwayland-client.so"])?; let wl_display_connect: libloading::Symbol = client_library .get(c"wl_display_connect".to_bytes()) - .unwrap(); + .inspect_err(|err| log::error!("Failed to load wl_display_connect: {err:?}")) + .ok()?; let wl_display_disconnect: libloading::Symbol = client_library .get(c"wl_display_disconnect".to_bytes()) - .unwrap(); + .inspect_err(|err| log::error!("Failed to load wl_display_disconnect: {err:?}")) + .ok()?; let display = ptr::NonNull::new(wl_display_connect(ptr::null()))?; wl_display_disconnect(display.as_ptr()); find_library(&["libwayland-egl.so.1", "libwayland-egl.so"])? @@ -394,9 +403,9 @@ impl<'a> core::ops::Deref for AdapterContextLock<'a> { impl<'a> Drop for AdapterContextLock<'a> { fn drop(&mut self) { if let Some(egl) = self.egl.take() { - egl.instance - .make_current(egl.display, None, None, None) - .unwrap(); + if let Err(err) = egl.instance.make_current(egl.display, None, None, None) { + log::error!("Failed to make EGL context current: {err:?}"); + } } } } @@ -501,6 +510,12 @@ fn terminate_display( } } +fn instance_err( + message: impl Into, +) -> impl FnOnce(E) -> crate::InstanceError { + move |e| crate::InstanceError::with_source(message.into(), e) +} + impl Inner { fn create( flags: wgt::InstanceFlags, @@ -508,18 +523,14 @@ impl Inner { display: khronos_egl::Display, force_gles_minor_version: wgt::Gles3MinorVersion, ) -> Result { - let version = initialize_display(&egl, display).map_err(|e| { - crate::InstanceError::with_source( - String::from("failed to initialize EGL display connection"), - e, - ) - })?; + let version = initialize_display(&egl, display) + .map_err(instance_err("failed to initialize EGL display connection"))?; let vendor = egl .query_string(Some(display), khronos_egl::VENDOR) - .unwrap(); + .map_err(instance_err("failed to query EGL vendor"))?; let display_extensions = egl .query_string(Some(display), khronos_egl::EXTENSIONS) - .unwrap() + .map_err(instance_err("failed to query EGL display extensions"))? .to_string_lossy(); log::debug!("Display vendor {vendor:?}, version {version:?}",); log::debug!( @@ -540,16 +551,19 @@ impl Inner { if log::max_level() >= log::LevelFilter::Trace { log::trace!("Configurations:"); - let config_count = egl.get_config_count(display).unwrap(); + let config_count = egl + .get_config_count(display) + .map_err(instance_err("failed to get config count"))?; let mut configurations = Vec::with_capacity(config_count); - egl.get_configs(display, &mut configurations).unwrap(); + egl.get_configs(display, &mut configurations) + .map_err(instance_err("failed to get configs"))?; for &config in configurations.iter() { - log::trace!("\tCONFORMANT=0x{:X}, RENDERABLE=0x{:X}, NATIVE_RENDERABLE=0x{:X}, SURFACE_TYPE=0x{:X}, ALPHA_SIZE={}", - egl.get_config_attrib(display, config, khronos_egl::CONFORMANT).unwrap(), - egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE).unwrap(), - egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE).unwrap(), - egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE).unwrap(), - egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE).unwrap(), + log::trace!("\tCONFORMANT=0x{:X?}, RENDERABLE=0x{:X?}, NATIVE_RENDERABLE=0x{:X?}, SURFACE_TYPE=0x{:X?}, ALPHA_SIZE={:?}", + egl.get_config_attrib(display, config, khronos_egl::CONFORMANT), + egl.get_config_attrib(display, config, khronos_egl::RENDERABLE_TYPE), + egl.get_config_attrib(display, config, khronos_egl::NATIVE_RENDERABLE), + egl.get_config_attrib(display, config, khronos_egl::SURFACE_TYPE), + egl.get_config_attrib(display, config, khronos_egl::ALPHA_SIZE), ); } } @@ -559,7 +573,7 @@ impl Inner { let supports_opengl = if version >= (1, 4) { let client_apis = egl .query_string(Some(display), khronos_egl::CLIENT_APIS) - .unwrap() + .map_err(instance_err("failed to query EGL client APIs string"))? .to_string_lossy(); client_apis .split(' ') @@ -572,7 +586,7 @@ impl Inner { } else { khronos_egl::OPENGL_ES_API }) - .unwrap(); + .map_err(instance_err("failed to bind API"))?; let mut khr_context_flags = 0; let supports_khr_context = display_extensions.contains("EGL_KHR_create_context"); @@ -661,7 +675,7 @@ impl Inner { let result = if supports_opengl { egl.create_context(display, config, None, &gl_context_attributes) .or_else(|_| { - egl.bind_api(khronos_egl::OPENGL_ES_API).unwrap(); + egl.bind_api(khronos_egl::OPENGL_ES_API)?; egl.create_context(display, config, None, &gles_context_attributes) }) } else { @@ -848,15 +862,9 @@ impl crate::Instance for Instance { } else { unsafe { khronos_egl::DynamicInstance::::load_required() } }; - let egl = match egl_result { - Ok(egl) => Arc::new(egl), - Err(e) => { - return Err(crate::InstanceError::with_source( - String::from("unable to open libEGL"), - e, - )); - } - }; + let egl = egl_result + .map(Arc::new) + .map_err(instance_err("unable to open libEGL"))?; let client_extensions = egl.query_string(None, khronos_egl::EXTENSIONS); @@ -891,75 +899,83 @@ impl crate::Instance for Instance { #[cfg(Emscripten)] let egl1_5: Option<&Arc> = Some(&egl); - let (display, display_owner, wsi_kind) = - if let (Some(library), Some(egl)) = (wayland_library, egl1_5) { - log::info!("Using Wayland platform"); - let display_attributes = [khronos_egl::ATTRIB_NONE]; - let display = unsafe { - egl.get_platform_display( - EGL_PLATFORM_WAYLAND_KHR, - khronos_egl::DEFAULT_DISPLAY, - &display_attributes, - ) - } - .unwrap(); - (display, Some(Rc::new(library)), WindowKind::Wayland) - } else if let (Some(display_owner), Some(egl)) = (x11_display_library, egl1_5) { - log::info!("Using X11 platform"); - let display_attributes = [khronos_egl::ATTRIB_NONE]; - let display = unsafe { - egl.get_platform_display( - EGL_PLATFORM_X11_KHR, - display_owner.display.as_ptr(), - &display_attributes, - ) - } - .unwrap(); - (display, Some(Rc::new(display_owner)), WindowKind::X11) - } else if let (Some(display_owner), Some(egl)) = (angle_x11_display_library, egl1_5) { - log::info!("Using Angle platform with X11"); - let display_attributes = [ - EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib, - EGL_PLATFORM_X11_KHR as khronos_egl::Attrib, - EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib, - usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)), - khronos_egl::ATTRIB_NONE, - ]; - let display = unsafe { - egl.get_platform_display( - EGL_PLATFORM_ANGLE_ANGLE, - display_owner.display.as_ptr(), - &display_attributes, - ) - } - .unwrap(); - (display, Some(Rc::new(display_owner)), WindowKind::AngleX11) - } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") { - log::warn!("No windowing system present. Using surfaceless platform"); - #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten - let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless"); - let display = unsafe { - egl.get_platform_display( - EGL_PLATFORM_SURFACELESS_MESA, - khronos_egl::DEFAULT_DISPLAY, - &[khronos_egl::ATTRIB_NONE], - ) - } - .unwrap(); + let (display, display_owner, wsi_kind) = if let (Some(library), Some(egl)) = + (wayland_library, egl1_5) + { + log::info!("Using Wayland platform"); + let display_attributes = [khronos_egl::ATTRIB_NONE]; + let display = unsafe { + egl.get_platform_display( + EGL_PLATFORM_WAYLAND_KHR, + khronos_egl::DEFAULT_DISPLAY, + &display_attributes, + ) + } + .map_err(instance_err("failed to get Wayland display"))?; + (display, Some(Rc::new(library)), WindowKind::Wayland) + } else if let (Some(display_owner), Some(egl)) = (x11_display_library, egl1_5) { + log::info!("Using X11 platform"); + let display_attributes = [khronos_egl::ATTRIB_NONE]; + let display = unsafe { + egl.get_platform_display( + EGL_PLATFORM_X11_KHR, + display_owner.display.as_ptr(), + &display_attributes, + ) + } + .map_err(instance_err("failed to get x11 display"))?; + (display, Some(Rc::new(display_owner)), WindowKind::X11) + } else if let (Some(display_owner), Some(egl)) = (angle_x11_display_library, egl1_5) { + log::info!("Using Angle platform with X11"); + let display_attributes = [ + EGL_PLATFORM_ANGLE_NATIVE_PLATFORM_TYPE_ANGLE as khronos_egl::Attrib, + EGL_PLATFORM_X11_KHR as khronos_egl::Attrib, + EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED as khronos_egl::Attrib, + usize::from(desc.flags.contains(wgt::InstanceFlags::VALIDATION)), + khronos_egl::ATTRIB_NONE, + ]; + let display = unsafe { + egl.get_platform_display( + EGL_PLATFORM_ANGLE_ANGLE, + display_owner.display.as_ptr(), + &display_attributes, + ) + } + .map_err(instance_err("failed to get Angle display"))?; + (display, Some(Rc::new(display_owner)), WindowKind::AngleX11) + } else if client_ext_str.contains("EGL_MESA_platform_surfaceless") { + log::warn!("No windowing system present. Using surfaceless platform"); + #[allow(clippy::unnecessary_literal_unwrap)] // This is only a literal on Emscripten + let egl = egl1_5.expect("Failed to get EGL 1.5 for surfaceless"); + let display = unsafe { + egl.get_platform_display( + EGL_PLATFORM_SURFACELESS_MESA, + khronos_egl::DEFAULT_DISPLAY, + &[khronos_egl::ATTRIB_NONE], + ) + } + .map_err(instance_err("failed to get MESA display"))?; - (display, None, WindowKind::Unknown) - } else { - log::warn!("EGL_MESA_platform_surfaceless not available. Using default platform"); - let display = unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) }.unwrap(); - (display, None, WindowKind::Unknown) - }; + (display, None, WindowKind::Unknown) + } else { + log::warn!("EGL_MESA_platform_surfaceless not available. Using default platform"); + let display = unsafe { egl.get_display(khronos_egl::DEFAULT_DISPLAY) } + .ok_or_else(|| crate::InstanceError::new("Failed to get default display".into()))?; + (display, None, WindowKind::Unknown) + }; if desc.flags.contains(wgt::InstanceFlags::VALIDATION) && client_ext_str.contains("EGL_KHR_debug") { log::debug!("Enabling EGL debug output"); let function: EglDebugMessageControlFun = { - let addr = egl.get_proc_address("eglDebugMessageControlKHR").unwrap(); + let addr = egl + .get_proc_address("eglDebugMessageControlKHR") + .ok_or_else(|| { + crate::InstanceError::new( + "failed to get `eglDebugMessageControlKHR` proc address".into(), + ) + })?; unsafe { core::mem::transmute(addr) } }; let attributes = [ @@ -1021,7 +1037,7 @@ impl crate::Instance for Instance { inner.config, khronos_egl::NATIVE_VISUAL_ID, ) - .unwrap(); + .map_err(instance_err("failed to get config NATIVE_VISUAL_ID"))?; let ret = unsafe { ndk_sys::ANativeWindow_setBuffersGeometry( @@ -1064,14 +1080,18 @@ impl crate::Instance for Instance { .egl .instance .upcast::() - .unwrap() + .ok_or_else(|| { + crate::InstanceError::new( + "EGL 1.5 is required for Wayland support".into(), + ) + })? .get_platform_display( EGL_PLATFORM_WAYLAND_KHR, display_handle.display.as_ptr(), &display_attributes, ) } - .unwrap(); + .map_err(instance_err("failed to get wayland display"))?; let new_inner = Inner::create( self.flags, @@ -1237,7 +1257,9 @@ impl Surface { ) -> Result<(), crate::SurfaceError> { let gl = unsafe { context.get_without_egl_lock() }; let swapchain = self.swapchain.read(); - let sc = swapchain.as_ref().unwrap(); + let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other( + "Surface has no swap-chain configured", + ))?; self.egl .instance @@ -1363,9 +1385,20 @@ impl crate::Surface for Surface { } (WindowKind::Unknown, Rwh::OhosNdk(handle)) => handle.native_window.as_ptr(), (WindowKind::Wayland, Rwh::Wayland(handle)) => { - let library = &self.wsi.display_owner.as_ref().unwrap().library; + let library = &self + .wsi + .display_owner + .as_ref() + .ok_or(crate::SurfaceError::Other("No WSI display owner"))? + .library; let wl_egl_window_create: libloading::Symbol = - unsafe { library.get(c"wl_egl_window_create".to_bytes()) }.unwrap(); + unsafe { library.get(c"wl_egl_window_create".to_bytes()) }.map_err( + |_| { + crate::SurfaceError::Other( + "Failed to load `wl_egl_window_create", + ) + }, + )?; let window = unsafe { wl_egl_window_create(handle.surface.as_ptr(), 640, 480) } .cast(); @@ -1475,9 +1508,16 @@ impl crate::Surface for Surface { }; if let Some(window) = wl_window { - let library = &self.wsi.display_owner.as_ref().unwrap().library; - let wl_egl_window_resize: libloading::Symbol = - unsafe { library.get(c"wl_egl_window_resize".to_bytes()) }.unwrap(); + let library = &self + .wsi + .display_owner + .as_ref() + .ok_or(crate::SurfaceError::Other("No WSI display owner"))? + .library; + let wl_egl_window_resize: libloading::Symbol = unsafe { + library.get(c"wl_egl_window_resize".to_bytes()) + } + .map_err(|_| crate::SurfaceError::Other("Failed to load `wl_egl_window_resize"))?; unsafe { wl_egl_window_resize( window, @@ -1561,7 +1601,9 @@ impl crate::Surface for Surface { _fence: &super::Fence, ) -> Result>, crate::SurfaceError> { let swapchain = self.swapchain.read(); - let sc = swapchain.as_ref().unwrap(); + let sc = swapchain.as_ref().ok_or(crate::SurfaceError::Other( + "Surface has no swap-chain configured", + ))?; let texture = super::Texture { inner: super::TextureInner::Renderbuffer { raw: sc.renderbuffer,