Merge pull request #272 from cmeissl/allow_multiple_egl_platforms
Dynamically select the EGL platform display
This commit is contained in:
commit
e4f72f7516
3
build.rs
3
build.rs
|
@ -18,8 +18,11 @@ fn gl_generate() {
|
||||||
"EGL_KHR_create_context_no_error",
|
"EGL_KHR_create_context_no_error",
|
||||||
"EGL_KHR_no_config_context",
|
"EGL_KHR_no_config_context",
|
||||||
"EGL_EXT_platform_base",
|
"EGL_EXT_platform_base",
|
||||||
|
"EGL_KHR_platform_x11",
|
||||||
"EGL_EXT_platform_x11",
|
"EGL_EXT_platform_x11",
|
||||||
|
"EGL_KHR_platform_wayland",
|
||||||
"EGL_EXT_platform_wayland",
|
"EGL_EXT_platform_wayland",
|
||||||
|
"EGL_KHR_platform_gbm",
|
||||||
"EGL_MESA_platform_gbm",
|
"EGL_MESA_platform_gbm",
|
||||||
"EGL_WL_bind_wayland_display",
|
"EGL_WL_bind_wayland_display",
|
||||||
"EGL_KHR_image_base",
|
"EGL_KHR_image_base",
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
|
use std::fmt;
|
||||||
use std::mem::MaybeUninit;
|
use std::mem::MaybeUninit;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use libc::c_void;
|
||||||
use nix::libc::c_int;
|
use nix::libc::c_int;
|
||||||
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
|
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
|
||||||
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
|
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
|
||||||
|
@ -61,6 +63,85 @@ pub struct EGLDisplay {
|
||||||
logger: slog::Logger,
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
error!(log, "Unable to find suitable EGL platform");
|
||||||
|
Err(Error::DisplayNotSupported)
|
||||||
|
}
|
||||||
|
|
||||||
impl EGLDisplay {
|
impl EGLDisplay {
|
||||||
/// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay)
|
/// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay)
|
||||||
pub fn new<N, L>(native: &N, logger: L) -> Result<EGLDisplay, Error>
|
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"));
|
let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));
|
||||||
ffi::make_sure_egl_is_loaded();
|
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
|
// we create an EGLDisplay
|
||||||
let display = unsafe {
|
let display = select_platform_display(native, &log)?;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can then query the egl api version
|
// We can then query the egl api version
|
||||||
let egl_version = {
|
let egl_version = {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use super::{display::EGLDisplayHandle, ffi, wrap_egl_call, SwapBuffersError};
|
||||||
use nix::libc::{c_int, c_void};
|
use nix::libc::{c_int, c_void};
|
||||||
#[cfg(feature = "backend_gbm")]
|
#[cfg(feature = "backend_gbm")]
|
||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::sync::Arc;
|
use std::{fmt::Debug, marker::PhantomData, sync::Arc};
|
||||||
|
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
use wayland_egl as wegl;
|
use wayland_egl as wegl;
|
||||||
|
@ -16,12 +16,112 @@ use winit::window::Window as WinitWindow;
|
||||||
#[cfg(feature = "backend_gbm")]
|
#[cfg(feature = "backend_gbm")]
|
||||||
use gbm::{AsRaw, Device as GbmDevice};
|
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
|
||||||
|
///
|
||||||
|
/// ```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, native_display, &["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
|
||||||
|
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> 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
|
||||||
|
///
|
||||||
|
/// ```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,
|
||||||
|
platform_name: &'static str,
|
||||||
|
native_display: *mut c_void,
|
||||||
|
attrib_list: Vec<ffi::EGLint>,
|
||||||
|
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")
|
||||||
|
.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.
|
/// 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 {
|
pub trait EGLNativeDisplay: Send {
|
||||||
/// Required extensions to use this platform
|
/// 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 required_extensions(&self) -> &'static [&'static str];
|
fn supported_platforms<'a>(&'a self) -> Vec<EGLPlatform<'a>>;
|
||||||
/// 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>);
|
|
||||||
/// Type of surfaces created
|
/// Type of surfaces created
|
||||||
fn surface_type(&self) -> ffi::EGLint {
|
fn surface_type(&self) -> ffi::EGLint {
|
||||||
ffi::egl::WINDOW_BIT as ffi::EGLint
|
ffi::egl::WINDOW_BIT as ffi::EGLint
|
||||||
|
@ -30,43 +130,33 @@ pub trait EGLNativeDisplay: Send {
|
||||||
|
|
||||||
#[cfg(feature = "backend_gbm")]
|
#[cfg(feature = "backend_gbm")]
|
||||||
impl<A: AsRawFd + Send + 'static> EGLNativeDisplay for GbmDevice<A> {
|
impl<A: AsRawFd + Send + 'static> EGLNativeDisplay for GbmDevice<A> {
|
||||||
fn required_extensions(&self) -> &'static [&'static str] {
|
fn supported_platforms<'a>(&'a self) -> Vec<EGLPlatform<'a>> {
|
||||||
&["EGL_MESA_platform_gbm"]
|
vec![
|
||||||
}
|
// see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_gbm.txt
|
||||||
fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>) {
|
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
|
||||||
ffi::egl::PLATFORM_GBM_MESA,
|
egl_platform!(PLATFORM_GBM_MESA, self.as_raw(), &["EGL_MESA_platform_gbm"]),
|
||||||
self.as_raw() as *mut _,
|
]
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "backend_winit")]
|
#[cfg(feature = "backend_winit")]
|
||||||
impl EGLNativeDisplay for WinitWindow {
|
impl EGLNativeDisplay for WinitWindow {
|
||||||
fn required_extensions(&self) -> &'static [&'static str] {
|
fn supported_platforms<'a>(&'a self) -> Vec<EGLPlatform<'a>> {
|
||||||
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>) {
|
|
||||||
if let Some(display) = self.wayland_display() {
|
if let Some(display) = self.wayland_display() {
|
||||||
(
|
vec![
|
||||||
ffi::egl::PLATFORM_WAYLAND_EXT,
|
// see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_wayland.txt
|
||||||
display as *mut _,
|
egl_platform!(PLATFORM_WAYLAND_KHR, display, &["EGL_KHR_platform_wayland"]),
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
// 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() {
|
} else if let Some(display) = self.xlib_display() {
|
||||||
(
|
vec![
|
||||||
ffi::egl::PLATFORM_X11_EXT,
|
// see: https://www.khronos.org/registry/EGL/extensions/KHR/EGL_KHR_platform_x11.txt
|
||||||
display as *mut _,
|
egl_platform!(PLATFORM_X11_KHR, display, &["EGL_KHR_platform_x11"]),
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
// 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 {
|
} else {
|
||||||
unreachable!("No backends for winit other then Wayland and X11 are supported")
|
unreachable!("No backends for winit other then Wayland and X11 are supported")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue