From 2e55501dea557114276801cfc1acc195086b1624 Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Thu, 13 May 2021 19:38:32 +0200 Subject: [PATCH 1/4] dynamically select the EGL platform display --- build.rs | 1 + src/backend/egl/display.rs | 114 ++++++++++++++++++++++++++----------- src/backend/egl/native.rs | 99 ++++++++++++++++++++------------ 3 files changed, 147 insertions(+), 67 deletions(-) diff --git a/build.rs b/build.rs index 1688225..650bab6 100644 --- a/build.rs +++ b/build.rs @@ -20,6 +20,7 @@ fn gl_generate() { "EGL_EXT_platform_base", "EGL_EXT_platform_x11", "EGL_EXT_platform_wayland", + "EGL_KHR_platform_gbm", "EGL_MESA_platform_gbm", "EGL_WL_bind_wayland_display", "EGL_KHR_image_base", diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 0f585e7..dd59665 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -2,10 +2,12 @@ use std::collections::HashSet; use std::ffi::CStr; +use std::fmt; use std::mem::MaybeUninit; use std::ops::Deref; use std::sync::Arc; +use libc::c_void; use nix::libc::c_int; #[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))] use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; @@ -61,6 +63,85 @@ pub struct EGLDisplay { logger: slog::Logger, } +fn select_platform_display( + native: &N, + log: &::slog::Logger, +) -> Result<*const c_void, Error> { + let dp_extensions = unsafe { + let p = wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)) + .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported + + // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise + // `eglQueryString` returns an error + if p.is_null() { + return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); + } else { + let p = CStr::from_ptr(p); + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } + }; + debug!(log, "Supported EGL client extensions: {:?}", dp_extensions); + + for platform in native.supported_platforms() { + debug!(log, "Trying EGL platform: {}", platform.platform_name); + + let log = log.new(o!("platform" => format!("{:?}", platform))); + + let missing_extensions = platform + .required_extensions + .iter() + .filter(|ext| !dp_extensions.iter().any(|x| x == *ext)) + .collect::>(); + + if !missing_extensions.is_empty() { + info!( + log, + "Skipping EGL platform because one or more required extensions are not supported. Missing extensions: {:?}", missing_extensions + ); + continue; + } + + let display = unsafe { + wrap_egl_call(|| { + ffi::egl::GetPlatformDisplayEXT( + platform.platform, + platform.native_display, + platform.attrib_list.as_ptr(), + ) + }) + .map_err(Error::DisplayCreationError) + }; + + let display = match display { + Ok(display) => { + if display == ffi::egl::NO_DISPLAY { + info!(log, "Skipping platform because the display is not supported"); + continue; + } + + display + } + Err(err) => { + info!( + log, + "Skipping platform because of an display creation error: {:?}", err + ); + continue; + } + }; + + info!( + log, + "Successfully selected EGL platform: {}", platform.platform_name + ); + return Ok(display); + } + + crit!(log, "Unable to find suitable EGL platform"); + Err(Error::DisplayNotSupported) +} + impl EGLDisplay { /// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay) pub fn new(native: &N, logger: L) -> Result @@ -71,39 +152,8 @@ impl EGLDisplay { let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl")); ffi::make_sure_egl_is_loaded(); - // the first step is to query the list of extensions without any display, if supported - let dp_extensions = unsafe { - let p = - wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)) - .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported - - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error - if p.is_null() { - return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); - } else { - let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - debug!(log, "Supported EGL client extensions: {:?}", dp_extensions); - - for ext in native.required_extensions() { - if !dp_extensions.iter().any(|x| x == ext) { - return Err(Error::EglExtensionNotSupported(native.required_extensions())); - } - } - - let (platform, native_ptr, attributes) = native.platform_display(); // we create an EGLDisplay - let display = unsafe { - wrap_egl_call(|| ffi::egl::GetPlatformDisplayEXT(platform, native_ptr, attributes.as_ptr())) - .map_err(Error::DisplayCreationError)? - }; - if display == ffi::egl::NO_DISPLAY { - return Err(Error::DisplayNotSupported); - } + let display = select_platform_display(native, &log)?; // We can then query the egl api version let egl_version = { diff --git a/src/backend/egl/native.rs b/src/backend/egl/native.rs index 37c58f2..7a4fca0 100644 --- a/src/backend/egl/native.rs +++ b/src/backend/egl/native.rs @@ -4,7 +4,7 @@ use super::{display::EGLDisplayHandle, ffi, wrap_egl_call, SwapBuffersError}; use nix::libc::{c_int, c_void}; #[cfg(feature = "backend_gbm")] use std::os::unix::io::AsRawFd; -use std::sync::Arc; +use std::{fmt::Debug, marker::PhantomData, sync::Arc}; #[cfg(feature = "backend_winit")] use wayland_egl as wegl; @@ -16,12 +16,35 @@ use winit::window::Window as WinitWindow; #[cfg(feature = "backend_gbm")] use gbm::{AsRaw, Device as GbmDevice}; +/// Type, Raw handle and attributes used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) +pub struct EGLPlatform<'a> { + /// Required extensions to use this platform + pub required_extensions: &'static [&'static str], + /// Human readable name of the platform + pub platform_name: &'static str, + /// Platform type used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) + pub platform: ffi::egl::types::EGLenum, + /// Raw native display handle used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) + pub native_display: *mut c_void, + /// Attributes used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) + pub attrib_list: Vec, + _phantom: PhantomData<&'a c_void>, +} + +impl<'a> Debug for EGLPlatform<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EGLPlatform") + .field("platform_name", &self.platform_name) + .field("required_extensions", &self.required_extensions) + .finish() + } +} + /// Trait describing platform specific functionality to create a valid `EGLDisplay` using the `EGL_EXT_platform_base`(https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) extension. pub trait EGLNativeDisplay: Send { - /// Required extensions to use this platform - fn required_extensions(&self) -> &'static [&'static str]; - /// Type, Raw handle and attributes used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) - fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec); + /// List of supported platforms that can be used to create a display using [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) + fn supported_platforms<'a>(&'a self) -> Vec>; + /// Type of surfaces created fn surface_type(&self) -> ffi::EGLint { ffi::egl::WINDOW_BIT as ffi::EGLint @@ -30,43 +53,49 @@ pub trait EGLNativeDisplay: Send { #[cfg(feature = "backend_gbm")] impl EGLNativeDisplay for GbmDevice { - fn required_extensions(&self) -> &'static [&'static str] { - &["EGL_MESA_platform_gbm"] - } - fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec) { - ( - ffi::egl::PLATFORM_GBM_MESA, - self.as_raw() as *mut _, - vec![ffi::egl::NONE as ffi::EGLint], - ) + fn supported_platforms<'a>(&'a self) -> Vec> { + vec![ + EGLPlatform { + required_extensions: &["EGL_KHR_platform_gbm"], + platform_name: "PLATFORM_GBM_KHR", + platform: ffi::egl::PLATFORM_GBM_KHR, + native_display: self.as_raw() as *mut _, + attrib_list: vec![ffi::egl::NONE as ffi::EGLint], + _phantom: PhantomData, + }, + EGLPlatform { + required_extensions: &["EGL_MESA_platform_gbm"], + platform_name: "PLATFORM_GBM_MESA", + platform: ffi::egl::PLATFORM_GBM_MESA, + native_display: self.as_raw() as *mut _, + attrib_list: vec![ffi::egl::NONE as ffi::EGLint], + _phantom: PhantomData, + }, + ] } } #[cfg(feature = "backend_winit")] impl EGLNativeDisplay for WinitWindow { - fn required_extensions(&self) -> &'static [&'static str] { - if self.wayland_display().is_some() { - &["EGL_EXT_platform_wayland"] - } else if self.xlib_display().is_some() { - &["EGL_EXT_platform_x11"] - } else { - unreachable!("No backends for winit other then Wayland and X11 are supported") - } - } - - fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec) { + fn supported_platforms<'a>(&'a self) -> Vec> { if let Some(display) = self.wayland_display() { - ( - ffi::egl::PLATFORM_WAYLAND_EXT, - display as *mut _, - vec![ffi::egl::NONE as ffi::EGLint], - ) + vec![EGLPlatform { + required_extensions: &["EGL_EXT_platform_wayland"], + platform_name: "PLATFORM_WAYLAND_EXT", + platform: ffi::egl::PLATFORM_WAYLAND_EXT, + native_display: display as *mut _, + attrib_list: vec![ffi::egl::NONE as ffi::EGLint], + _phantom: PhantomData, + }] } else if let Some(display) = self.xlib_display() { - ( - ffi::egl::PLATFORM_X11_EXT, - display as *mut _, - vec![ffi::egl::NONE as ffi::EGLint], - ) + vec![EGLPlatform { + required_extensions: &["EGL_EXT_platform_x11"], + platform_name: "PLATFORM_X11_EXT", + platform: ffi::egl::PLATFORM_X11_EXT, + native_display: display as *mut _, + attrib_list: vec![ffi::egl::NONE as ffi::EGLint], + _phantom: PhantomData, + }] } else { unreachable!("No backends for winit other then Wayland and X11 are supported") } From af66a9c152a13c1a6aaaec218c27765df89a31e3 Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Sat, 15 May 2021 23:04:46 +0200 Subject: [PATCH 2/4] add more supported egl platforms --- build.rs | 2 + src/backend/egl/native.rs | 122 +++++++++++++++++++++++++++----------- 2 files changed, 91 insertions(+), 33 deletions(-) diff --git a/build.rs b/build.rs index 650bab6..58e9097 100644 --- a/build.rs +++ b/build.rs @@ -18,7 +18,9 @@ fn gl_generate() { "EGL_KHR_create_context_no_error", "EGL_KHR_no_config_context", "EGL_EXT_platform_base", + "EGL_KHR_platform_x11", "EGL_EXT_platform_x11", + "EGL_KHR_platform_wayland", "EGL_EXT_platform_wayland", "EGL_KHR_platform_gbm", "EGL_MESA_platform_gbm", diff --git a/src/backend/egl/native.rs b/src/backend/egl/native.rs index 7a4fca0..ab08bd0 100644 --- a/src/backend/egl/native.rs +++ b/src/backend/egl/native.rs @@ -16,6 +16,44 @@ use winit::window::Window as WinitWindow; #[cfg(feature = "backend_gbm")] use gbm::{AsRaw, Device as GbmDevice}; +/// Create a `EGLPlatform<'a>` for the provided platform. +/// +/// # Arguments +/// +/// * `platform` - The platform defined in `ffi::egl::` +/// * `native_display` - The native display raw pointer which can be casted to `*mut c_void` +/// * `required_extensions` - The name of the required EGL Extension for this platform +/// +/// # Optional Arguments +/// * `attrib_list` - A list of `ffi::EGLint` like defined in the EGL Extension +/// +/// # Examples +/// +/// ``` +/// // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt +/// egl_platform!(PLATFORM_GBM_KHR, self.as_raw(), &["EGL_KHR_platform_gbm"]) +/// ``` +#[macro_export] +macro_rules! egl_platform { + ($platform:ident, $native_display:expr, $required_extensions:expr) => { + egl_platform!( + $platform, + $native_display, + $required_extensions, + vec![ffi::egl::NONE as ffi::EGLint] + ); + }; + ($platform:ident, $native_display:expr, $required_extensions:expr, $attrib_list:expr) => { + EGLPlatform::new( + ffi::egl::$platform, + stringify!($platform), + $native_display as *mut _, + $attrib_list, + $required_extensions, + ) + }; +} + /// Type, Raw handle and attributes used to call [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) pub struct EGLPlatform<'a> { /// Required extensions to use this platform @@ -31,6 +69,40 @@ pub struct EGLPlatform<'a> { _phantom: PhantomData<&'a c_void>, } +impl<'a> EGLPlatform<'a> { + /// Create a `EGLPlatform<'a>` for the provided platform. + /// + /// # Arguments + /// + /// * `platform` - The platform defined in `ffi::egl::` + /// * `platform_name` - A human readable representation of the platform + /// * `native_display` - The native display raw pointer which can be casted to `*mut c_void` + /// * `attrib_list` - A list of `ffi::EGLint` like defined in the EGL Extension + /// * `required_extensions` - The names of the required EGL Extensions for this platform + /// + /// # Examples + /// + /// ``` + /// EGLPlatform::new(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, vec![ffi::egl::NONE as ffi::EGLint], &["EGL_KHR_platform_gbm"]) + /// ``` + pub fn new( + platform: ffi::egl::types::EGLenum, + platform_name: &'static str, + native_display: *mut c_void, + attrib_list: Vec, + required_extensions: &'static [&'static str], + ) -> Self { + EGLPlatform { + platform, + platform_name, + native_display, + attrib_list, + required_extensions, + _phantom: PhantomData, + } + } +} + impl<'a> Debug for EGLPlatform<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("EGLPlatform") @@ -42,7 +114,7 @@ impl<'a> Debug for EGLPlatform<'a> { /// Trait describing platform specific functionality to create a valid `EGLDisplay` using the `EGL_EXT_platform_base`(https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) extension. pub trait EGLNativeDisplay: Send { - /// List of supported platforms that can be used to create a display using [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) + /// List of supported platforms that can be used to create a display using [`eglGetPlatformDisplayEXT`](https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_base.txt) fn supported_platforms<'a>(&'a self) -> Vec>; /// Type of surfaces created @@ -55,22 +127,10 @@ pub trait EGLNativeDisplay: Send { impl EGLNativeDisplay for GbmDevice { fn supported_platforms<'a>(&'a self) -> Vec> { vec![ - EGLPlatform { - required_extensions: &["EGL_KHR_platform_gbm"], - platform_name: "PLATFORM_GBM_KHR", - platform: ffi::egl::PLATFORM_GBM_KHR, - native_display: self.as_raw() as *mut _, - attrib_list: vec![ffi::egl::NONE as ffi::EGLint], - _phantom: PhantomData, - }, - EGLPlatform { - required_extensions: &["EGL_MESA_platform_gbm"], - platform_name: "PLATFORM_GBM_MESA", - platform: ffi::egl::PLATFORM_GBM_MESA, - native_display: self.as_raw() as *mut _, - attrib_list: vec![ffi::egl::NONE as ffi::EGLint], - _phantom: PhantomData, - }, + // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt + egl_platform!(PLATFORM_GBM_KHR, self.as_raw(), &["EGL_KHR_platform_gbm"]), + // see: https://www.khronos.org/registry/EGL/extensions/MESA/EGL_MESA_platform_gbm.txt + egl_platform!(PLATFORM_GBM_MESA, self.as_raw(), &["EGL_MESA_platform_gbm"]), ] } } @@ -79,23 +139,19 @@ impl EGLNativeDisplay for GbmDevice { impl EGLNativeDisplay for WinitWindow { fn supported_platforms<'a>(&'a self) -> Vec> { if let Some(display) = self.wayland_display() { - vec![EGLPlatform { - required_extensions: &["EGL_EXT_platform_wayland"], - platform_name: "PLATFORM_WAYLAND_EXT", - platform: ffi::egl::PLATFORM_WAYLAND_EXT, - native_display: display as *mut _, - attrib_list: vec![ffi::egl::NONE as ffi::EGLint], - _phantom: PhantomData, - }] + vec![ + // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_wayland.txt + egl_platform!(PLATFORM_WAYLAND_KHR, display, &["EGL_KHR_platform_wayland"]), + // see: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_wayland.txt + egl_platform!(PLATFORM_WAYLAND_EXT, display, &["EGL_EXT_platform_wayland"]), + ] } else if let Some(display) = self.xlib_display() { - vec![EGLPlatform { - required_extensions: &["EGL_EXT_platform_x11"], - platform_name: "PLATFORM_X11_EXT", - platform: ffi::egl::PLATFORM_X11_EXT, - native_display: display as *mut _, - attrib_list: vec![ffi::egl::NONE as ffi::EGLint], - _phantom: PhantomData, - }] + vec![ + // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_x11.txt + egl_platform!(PLATFORM_X11_KHR, display, &["EGL_KHR_platform_x11"]), + // see: https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_platform_x11.txt + egl_platform!(PLATFORM_X11_EXT, display, &["EGL_EXT_platform_x11"]), + ] } else { unreachable!("No backends for winit other then Wayland and X11 are supported") } From 8dc94e0817d487f9f380fbd8046587c1ceb22ad3 Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Sat, 15 May 2021 23:34:50 +0200 Subject: [PATCH 3/4] ignore eglplatform doc examples --- src/backend/egl/native.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/backend/egl/native.rs b/src/backend/egl/native.rs index ab08bd0..d00d846 100644 --- a/src/backend/egl/native.rs +++ b/src/backend/egl/native.rs @@ -29,9 +29,12 @@ use gbm::{AsRaw, Device as GbmDevice}; /// /// # Examples /// -/// ``` +/// ```ignore +/// use smithay::backend::egl::{ffi, native::EGLPlatform}; +/// use smithay::egl_platform; +/// /// // see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt -/// egl_platform!(PLATFORM_GBM_KHR, self.as_raw(), &["EGL_KHR_platform_gbm"]) +/// egl_platform!(PLATFORM_GBM_KHR, native_display, &["EGL_KHR_platform_gbm"]); /// ``` #[macro_export] macro_rules! egl_platform { @@ -82,8 +85,10 @@ impl<'a> EGLPlatform<'a> { /// /// # Examples /// - /// ``` - /// EGLPlatform::new(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, vec![ffi::egl::NONE as ffi::EGLint], &["EGL_KHR_platform_gbm"]) + /// ```ignore + /// use smithay::backend::egl::{ffi, native::EGLPlatform}; + /// + /// EGLPlatform::new(ffi::egl::PLATFORM_GBM_KHR, "PLATFORM_GBM_KHR", native_display as *mut _, vec![ffi::egl::NONE as ffi::EGLint], &["EGL_KHR_platform_gbm"]); /// ``` pub fn new( platform: ffi::egl::types::EGLenum, From f20ff8a178030bde6ab23c7ec7d61ce6cc342da3 Mon Sep 17 00:00:00 2001 From: Christian Meissl Date: Sun, 16 May 2021 10:58:29 +0200 Subject: [PATCH 4/4] reduce severity to error in case... ...no suitable egl platform could be selected --- src/backend/egl/display.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index dd59665..4e7e79c 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -138,7 +138,7 @@ fn select_platform_display( return Ok(display); } - crit!(log, "Unable to find suitable EGL platform"); + error!(log, "Unable to find suitable EGL platform"); Err(Error::DisplayNotSupported) }