dynamically select the EGL platform display
This commit is contained in:
parent
5d6fadcea8
commit
2e55501dea
1
build.rs
1
build.rs
|
@ -20,6 +20,7 @@ fn gl_generate() {
|
||||||
"EGL_EXT_platform_base",
|
"EGL_EXT_platform_base",
|
||||||
"EGL_EXT_platform_x11",
|
"EGL_EXT_platform_x11",
|
||||||
"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);
|
||||||
|
}
|
||||||
|
|
||||||
|
crit!(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,35 @@ 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};
|
||||||
|
|
||||||
|
/// 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.
|
/// 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 +53,49 @@ 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![
|
||||||
}
|
EGLPlatform {
|
||||||
fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>) {
|
required_extensions: &["EGL_KHR_platform_gbm"],
|
||||||
(
|
platform_name: "PLATFORM_GBM_KHR",
|
||||||
ffi::egl::PLATFORM_GBM_MESA,
|
platform: ffi::egl::PLATFORM_GBM_KHR,
|
||||||
self.as_raw() as *mut _,
|
native_display: self.as_raw() as *mut _,
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
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")]
|
#[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![EGLPlatform {
|
||||||
ffi::egl::PLATFORM_WAYLAND_EXT,
|
required_extensions: &["EGL_EXT_platform_wayland"],
|
||||||
display as *mut _,
|
platform_name: "PLATFORM_WAYLAND_EXT",
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
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() {
|
} else if let Some(display) = self.xlib_display() {
|
||||||
(
|
vec![EGLPlatform {
|
||||||
ffi::egl::PLATFORM_X11_EXT,
|
required_extensions: &["EGL_EXT_platform_x11"],
|
||||||
display as *mut _,
|
platform_name: "PLATFORM_X11_EXT",
|
||||||
vec![ffi::egl::NONE as ffi::EGLint],
|
platform: ffi::egl::PLATFORM_X11_EXT,
|
||||||
)
|
native_display: display as *mut _,
|
||||||
|
attrib_list: vec![ffi::egl::NONE as ffi::EGLint],
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}]
|
||||||
} 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