dynamically select the EGL platform display

This commit is contained in:
Christian Meissl 2021-05-13 19:38:32 +02:00
parent 5d6fadcea8
commit 2e55501dea
3 changed files with 147 additions and 67 deletions

View File

@ -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",

View File

@ -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<N: EGLNativeDisplay + 'static>(
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::<Vec<_>>()
}
};
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::<Vec<_>>();
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<N, L>(native: &N, logger: L) -> Result<EGLDisplay, Error>
@ -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::<Vec<_>>()
}
};
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 = {

View File

@ -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<ffi::EGLint>,
_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<ffi::EGLint>);
/// 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<EGLPlatform<'a>>;
/// 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<A: AsRawFd + Send + 'static> EGLNativeDisplay for GbmDevice<A> {
fn required_extensions(&self) -> &'static [&'static str] {
&["EGL_MESA_platform_gbm"]
}
fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>) {
(
ffi::egl::PLATFORM_GBM_MESA,
self.as_raw() as *mut _,
vec![ffi::egl::NONE as ffi::EGLint],
)
fn supported_platforms<'a>(&'a self) -> Vec<EGLPlatform<'a>> {
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<ffi::EGLint>) {
fn supported_platforms<'a>(&'a self) -> Vec<EGLPlatform<'a>> {
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")
}