Simplify egl platform code

- Remove EGLStream code, nvidia support will re-introduced,
  when 470 hopefully lands with GBM support this summer.
- Greately simplify the native code by setting exclusively on
  `EGL_EXT_platform_base` and its extensions for initialization.
- Remove generic parameters for the underlying objects on `EGLDisplay`
  and `EGLSurface` by using trait objects instead.
- Allow creation of `EGLContext`s without a config for surface-less usage.
This commit is contained in:
Victor Brekenfeld 2021-04-07 00:31:30 +02:00
parent d606165088
commit d99108a8e6
6 changed files with 347 additions and 546 deletions

View File

@ -1,96 +1,106 @@
//! EGL context related structs //! EGL context related structs
use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::egl::display::{EGLDisplay, EGLDisplayHandle};
use crate::backend::egl::native::NativeSurface;
use crate::backend::egl::{native, EGLSurface};
use crate::backend::graphics::PixelFormat;
use std::os::raw::c_int; use std::os::raw::c_int;
use std::ptr; use std::ptr;
use std::sync::{atomic::Ordering, Arc}; use std::sync::atomic::Ordering;
use super::{ffi, wrap_egl_call, Error, MakeCurrentError};
use crate::backend::egl::display::{EGLDisplay, PixelFormat};
use crate::backend::egl::native::EGLNativeSurface;
use crate::backend::egl::EGLSurface;
/// EGL context for rendering /// EGL context for rendering
#[derive(Debug)] #[derive(Debug)]
pub struct EGLContext { pub struct EGLContext {
context: ffi::egl::types::EGLContext, context: ffi::egl::types::EGLContext,
display: Arc<EGLDisplayHandle>, pub(crate) display: EGLDisplay,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat, pixel_format: Option<PixelFormat>,
} }
// EGLContexts can be moved between threads safely // EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {} unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {} unsafe impl Sync for EGLContext {}
impl EGLContext { impl EGLContext {
pub fn new<L>(
display: &EGLDisplay,
log: L,
) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
Self::new_internal(display, None, log)
}
/// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay) /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay)
pub(crate) fn new<B, N, L>( pub fn new_with_config<L>(
display: &EGLDisplay<B, N>, display: &EGLDisplay,
mut attributes: GlAttributes, attributes: GlAttributes,
reqs: PixelFormatRequirements, reqs: PixelFormatRequirements,
log: L, log: L,
) -> Result<EGLContext, Error> ) -> Result<EGLContext, Error>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
B: native::Backend,
N: native::NativeDisplay<B>,
{ {
let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "renderer_egl")); Self::new_internal(display, Some((attributes, reqs)), log)
}
// If no version is given, try OpenGLES 3.0, if available, fn new_internal<L>(
// fallback to 2.0 otherwise display: &EGLDisplay,
let version = match attributes.version { config: Option<(GlAttributes, PixelFormatRequirements)>,
Some((3, x)) => (3, x), log: L,
Some((2, x)) => (2, x), ) -> Result<EGLContext, Error>
where
L: Into<Option<::slog::Logger>>,
{
let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "backend_egl"));
let (pixel_format, config_id) = match config {
Some((attributes, reqs)) => {
let (format, config_id) = display.choose_config(attributes, reqs)?;
(Some(format), config_id)
},
None => { None => {
debug!(log, "Trying to initialize EGL with OpenGLES 3.0"); if !display.extensions.iter().any(|x| x == "EGL_KHR_no_config_context") &&
attributes.version = Some((3, 0)); !display.extensions.iter().any(|x| x == "EGL_MESA_configless_context") &&
match EGLContext::new(display, attributes, reqs, log.clone()) { !display.extensions.iter().any(|x| x == "EGL_KHR_surfaceless_context")
Ok(x) => return Ok(x), {
Err(err) => { return Err(Error::EglExtensionNotSupported(&["EGL_KHR_no_config_context", "EGL_MESA_configless_context", "EGL_KHR_surfaceless_context"]));
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
attributes.version = Some((2, 0));
return EGLContext::new(display, attributes, reqs, log.clone());
}
} }
} (None, ffi::egl::NO_CONFIG_KHR)
Some((1, x)) => {
error!(log, "OpenGLES 1.* is not supported by the EGL renderer backend");
return Err(Error::OpenGlVersionNotSupported((1, x)));
}
Some(version) => {
error!(
log,
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend", version
);
return Err(Error::OpenGlVersionNotSupported(version));
} }
}; };
let (pixel_format, config_id) = display.choose_config(attributes, reqs)?;
let mut context_attributes = Vec::with_capacity(10); let mut context_attributes = Vec::with_capacity(10);
if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") { if let Some((attributes, _)) = config {
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0); let version = attributes.version;
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
context_attributes.push(version.0 as i32);
trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32);
if attributes.debug && display.egl_version >= (1, 5) { if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE"); trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
context_attributes.push(ffi::egl::TRUE as i32); context_attributes.push(version.0 as i32);
trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32);
if attributes.debug && display.egl_version >= (1, 5) {
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
context_attributes.push(ffi::egl::TRUE as i32);
}
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
context_attributes.push(0);
} else if display.egl_version >= (1, 3) {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32);
} }
} else {
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
context_attributes.push(0);
} else if display.egl_version >= (1, 3) {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32); context_attributes.push(2);
} }
context_attributes.push(ffi::egl::NONE as i32); context_attributes.push(ffi::egl::NONE as i32);
@ -111,7 +121,7 @@ impl EGLContext {
Ok(EGLContext { Ok(EGLContext {
context, context,
display: display.display.clone(), display: display.clone(),
config_id, config_id,
pixel_format, pixel_format,
}) })
@ -124,12 +134,10 @@ impl EGLContext {
/// ///
/// This function is marked unsafe, because the context cannot be made current /// This function is marked unsafe, because the context cannot be made current
/// on multiple threads. /// on multiple threads.
pub unsafe fn make_current_with_surface<N>(&self, surface: &EGLSurface<N>) -> Result<(), MakeCurrentError> pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError>
where
N: NativeSurface,
{ {
let surface_ptr = surface.surface.load(Ordering::SeqCst); let surface_ptr = surface.surface.load(Ordering::SeqCst);
wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context)) wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display.display, surface_ptr, surface_ptr, self.context))
.map(|_| ()) .map(|_| ())
.map_err(Into::into) .map_err(Into::into)
} }
@ -143,7 +151,7 @@ impl EGLContext {
pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> { pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
wrap_egl_call(|| { wrap_egl_call(|| {
ffi::egl::MakeCurrent( ffi::egl::MakeCurrent(
**self.display, **self.display.display,
ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE,
ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE,
self.context, self.context,
@ -159,12 +167,12 @@ impl EGLContext {
} }
/// Returns the egl config for this context /// Returns the egl config for this context
pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id self.config_id
} }
/// Returns the pixel format of the main framebuffer of the context. /// Returns the pixel format of the main framebuffer of the context.
pub fn get_pixel_format(&self) -> PixelFormat { pub fn pixel_format(&self) -> Option<PixelFormat> {
self.pixel_format self.pixel_format
} }
@ -175,7 +183,7 @@ impl EGLContext {
if self.is_current() { if self.is_current() {
wrap_egl_call(|| unsafe { wrap_egl_call(|| unsafe {
ffi::egl::MakeCurrent( ffi::egl::MakeCurrent(
**self.display, **self.display.display,
ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE,
ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE,
ffi::egl::NO_CONTEXT, ffi::egl::NO_CONTEXT,
@ -192,7 +200,7 @@ impl Drop for EGLContext {
// We need to ensure the context is unbound, otherwise it egl stalls the destroy call // We need to ensure the context is unbound, otherwise it egl stalls the destroy call
// ignore failures at this point // ignore failures at this point
let _ = self.unbind(); let _ = self.unbind();
ffi::egl::DestroyContext(**self.display, self.context); ffi::egl::DestroyContext(**self.display.display, self.context);
} }
} }
} }
@ -202,9 +210,9 @@ impl Drop for EGLContext {
pub struct GlAttributes { pub struct GlAttributes {
/// Describes the OpenGL API and version that are being requested when a context is created. /// Describes the OpenGL API and version that are being requested when a context is created.
/// ///
/// `Some(3, 0)` will request a OpenGL ES 3.0 context for example. /// `(3, 0)` will request a OpenGL ES 3.0 context for example.
/// `None` means "don't care" (minimum will be 2.0). /// `(2, 0)` is the minimum.
pub version: Option<(u8, u8)>, pub version: (u8, u8),
/// OpenGL profile to use /// OpenGL profile to use
pub profile: Option<GlProfile>, pub profile: Option<GlProfile>,
/// Whether to enable the debug flag of the context. /// Whether to enable the debug flag of the context.

View File

@ -1,31 +1,24 @@
//! Type safe native types for safe egl initialisation //! Type safe native types for safe egl initialisation
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::EGLGraphicsBackend;
use crate::backend::egl::{
ffi, get_proc_address, native, wrap_egl_call, BufferAccessError, EGLContext, EGLError, EGLImages,
EGLSurface, Error, Format, SurfaceCreationError,
};
use std::sync::Arc; use std::sync::Arc;
use std::ffi::CStr;
use std::mem::MaybeUninit;
use std::ops::Deref;
use nix::libc::c_int; use nix::libc::c_int;
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
#[cfg(feature = "wayland_frontend")]
use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use wayland_sys::server::wl_display; use wayland_sys::server::wl_display;
use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements}; use crate::backend::allocator::{Buffer, dmabuf::Dmabuf};
#[cfg(feature = "renderer_gl")] use crate::backend::egl::{
use crate::backend::graphics::gl::ffi as gl_ffi; ffi::egl::types::EGLImage,
use crate::backend::graphics::PixelFormat; ffi, wrap_egl_call, EGLError, Error,
use std::cell::{Ref, RefCell, RefMut}; context::{GlAttributes, PixelFormatRequirements},
use std::ffi::CStr; native::{EGLNativeDisplay},
use std::marker::PhantomData; BufferAccessError, EGLImages, Format,
use std::mem::MaybeUninit; };
use std::fmt;
use std::ops::Deref;
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed /// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
/// once all resources bound to it have been dropped. /// once all resources bound to it have been dropped.
@ -56,55 +49,54 @@ impl Drop for EGLDisplayHandle {
} }
/// [`EGLDisplay`] represents an initialised EGL environment /// [`EGLDisplay`] represents an initialised EGL environment
#[derive(Debug)] #[derive(Clone)]
pub struct EGLDisplay<B: native::Backend, N: native::NativeDisplay<B>> { pub struct EGLDisplay {
native: RefCell<N>,
pub(crate) display: Arc<EGLDisplayHandle>, pub(crate) display: Arc<EGLDisplayHandle>,
pub(crate) egl_version: (i32, i32), pub(crate) egl_version: (i32, i32),
pub(crate) extensions: Vec<String>, pub(crate) extensions: Vec<String>,
surface_type: ffi::EGLint,
logger: slog::Logger, logger: slog::Logger,
_backend: PhantomData<B>,
} }
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> { 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<L>(native: N, logger: L) -> Result<EGLDisplay<B, N>, Error> pub fn new<N, L>(native: &N, logger: L) -> Result<EGLDisplay, Error>
where where
N: EGLNativeDisplay + 'static,
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "renderer_egl")); let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl"));
let ptr = native.ptr()?;
let egl_attribs = native.attributes();
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 // the first step is to query the list of extensions without any display, if supported
let dp_extensions = unsafe { let dp_extensions = unsafe {
let p = let p =
wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)) wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32))
.map_err(Error::InitFailed)?; .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 // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
// `eglQueryString` returns an error // `eglQueryString` returns an error
if p.is_null() { if p.is_null() {
vec![] return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"]));
} else { } else {
let p = CStr::from_ptr(p); let p = CStr::from_ptr(p);
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>() list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
} }
}; };
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions); 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 = unsafe {
B::get_display( wrap_egl_call(|| ffi::egl::GetPlatformDisplayEXT(platform, native_ptr, attributes.as_ptr()))
ptr, .map_err(Error::DisplayCreationError)?
&egl_attribs,
|e: &str| dp_extensions.iter().any(|s| s == e),
log.clone(),
)
.map_err(Error::DisplayCreationError)?
}; };
if display == ffi::egl::NO_DISPLAY { if display == ffi::egl::NO_DISPLAY {
return Err(Error::DisplayNotSupported); return Err(Error::DisplayNotSupported);
@ -143,7 +135,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
} else { } else {
vec![] vec![]
}; };
info!(log, "EGL Extensions: {:?}", extensions); info!(log, "Supported EGL display extensions: {:?}", extensions);
// egl <= 1.2 does not support OpenGL ES (maybe we want to support OpenGL in the future?) // egl <= 1.2 does not support OpenGL ES (maybe we want to support OpenGL in the future?)
if egl_version <= (1, 2) { if egl_version <= (1, 2) {
@ -153,12 +145,11 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
.map_err(|source| Error::OpenGlesNotSupported(Some(source)))?; .map_err(|source| Error::OpenGlesNotSupported(Some(source)))?;
Ok(EGLDisplay { Ok(EGLDisplay {
native: RefCell::new(native),
display: Arc::new(EGLDisplayHandle { handle: display }), display: Arc::new(EGLDisplayHandle { handle: display }),
surface_type: native.surface_type(),
egl_version, egl_version,
extensions, extensions,
logger: log, logger: log,
_backend: PhantomData,
}) })
} }
@ -170,7 +161,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> { ) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {
let descriptor = { let descriptor = {
let mut out: Vec<c_int> = Vec::with_capacity(37); let mut out: Vec<c_int> = Vec::with_capacity(37);
let surface_type = self.native.borrow().surface_type();
if self.egl_version >= (1, 2) { if self.egl_version >= (1, 2) {
trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER"); trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
@ -178,15 +168,13 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
out.push(ffi::egl::RGB_BUFFER as c_int); out.push(ffi::egl::RGB_BUFFER as c_int);
} }
trace!(self.logger, "Setting SURFACE_TYPE to {}", surface_type); trace!(self.logger, "Setting SURFACE_TYPE to {}", self.surface_type);
out.push(ffi::egl::SURFACE_TYPE as c_int); out.push(ffi::egl::SURFACE_TYPE as c_int);
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error out.push(self.surface_type);
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
out.push(surface_type);
match attributes.version { match attributes.version {
Some((3, _)) => { (3, _) => {
if self.egl_version < (1, 3) { if self.egl_version < (1, 3) {
error!( error!(
self.logger, self.logger,
@ -201,7 +189,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int); out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
} }
Some((2, _)) => { (2, _) => {
if self.egl_version < (1, 3) { if self.egl_version < (1, 3) {
error!( error!(
self.logger, self.logger,
@ -216,12 +204,9 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
out.push(ffi::egl::CONFORMANT as c_int); out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int); out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
} }
Some(ver) => { ver => {
return Err(Error::OpenGlVersionNotSupported(ver)); return Err(Error::OpenGlVersionNotSupported(ver));
} }
None => {
return Err(Error::OpenGlVersionNotSupported((0, 0)));
}
}; };
reqs.create_attributes(&mut out, &self.logger) reqs.create_attributes(&mut out, &self.logger)
@ -352,44 +337,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
Ok((desc, config_id)) Ok((desc, config_id))
} }
/// Create a new [`EGLContext`](::backend::egl::EGLContext)
pub fn create_context(
&self,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
) -> Result<EGLContext, Error> {
EGLContext::new(&self, attributes, reqs, self.logger.clone())
}
/// Creates a surface for rendering
pub fn create_surface(
&self,
pixel_format: PixelFormat,
double_buffer: Option<bool>,
config: ffi::egl::types::EGLConfig,
args: N::Arguments,
) -> Result<EGLSurface<B::Surface>, SurfaceCreationError<B::Error>> {
trace!(self.logger, "Creating EGL window surface.");
let surface = self
.native
.borrow_mut()
.create_surface(args)
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)?;
EGLSurface::new(
self.display.clone(),
pixel_format,
double_buffer,
config,
surface,
self.logger.clone(),
)
.map(|x| {
debug!(self.logger, "EGL surface successfully created");
x
})
}
/// Returns the runtime egl version of this display /// Returns the runtime egl version of this display
pub fn get_egl_version(&self) -> (i32, i32) { pub fn get_egl_version(&self) -> (i32, i32) {
self.egl_version self.egl_version
@ -400,28 +347,90 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
self.extensions.clone() self.extensions.clone()
} }
/// Borrow the underlying native display. /// Imports a dmabuf as an eglimage
/// pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base") &&
/// Multiple read-only borrows are possible. Borrowing the !self.extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import")
/// backend while there is a mutable reference will panic. {
pub fn borrow(&self) -> Ref<'_, N> { return Err(Error::EglExtensionNotSupported(&["EGL_KHR_image_base", "EGL_EXT_image_dma_buf_import"]));
self.native.borrow() }
if dmabuf.has_modifier() {
if !self.extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers") {
return Err(Error::EglExtensionNotSupported(&["EGL_EXT_image_dma_buf_import_modifiers"]));
}
};
let mut out: Vec<c_int> = Vec::with_capacity(50);
out.extend(&[
ffi::egl::WIDTH as i32, dmabuf.width() as i32,
ffi::egl::HEIGHT as i32, dmabuf.height() as i32,
ffi::egl::LINUX_DRM_FOURCC_EXT as i32, dmabuf.format().code as u32 as i32,
]);
let names = [
[
ffi::egl::DMA_BUF_PLANE0_FD_EXT,
ffi::egl::DMA_BUF_PLANE0_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE0_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE0_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE0_MODIFIER_HI_EXT
], [
ffi::egl::DMA_BUF_PLANE1_FD_EXT,
ffi::egl::DMA_BUF_PLANE1_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE1_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE1_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE1_MODIFIER_HI_EXT
], [
ffi::egl::DMA_BUF_PLANE2_FD_EXT,
ffi::egl::DMA_BUF_PLANE2_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE2_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE2_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE2_MODIFIER_HI_EXT
], [
ffi::egl::DMA_BUF_PLANE3_FD_EXT,
ffi::egl::DMA_BUF_PLANE3_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE3_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE3_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE3_MODIFIER_HI_EXT
]
];
for (i, ((fd, offset), stride)) in dmabuf.handles().iter().zip(dmabuf.offsets()).zip(dmabuf.strides()).enumerate() {
out.extend(&[
names[i][0] as i32, *fd,
names[i][1] as i32, *offset as i32,
names[i][2] as i32, *stride as i32,
]);
if dmabuf.has_modifier() {
out.extend(&[
names[i][3] as i32, (Into::<u64>::into(dmabuf.format().modifier) & 0xFFFFFFFF) as i32,
names[i][4] as i32, (Into::<u64>::into(dmabuf.format().modifier) >> 32) as i32,
])
}
}
out.push(ffi::egl::NONE as i32);
unsafe {
let image = ffi::egl::CreateImageKHR(
**self.display,
ffi::egl::NO_CONTEXT,
ffi::egl::LINUX_DMA_BUF_EXT,
std::ptr::null(),
out.as_ptr(),
);
if image == ffi::egl::NO_IMAGE_KHR {
Err(Error::EGLImageCreationFailed)
} else {
// TODO check for external
Ok(image)
}
}
} }
/// Borrow the underlying native display mutably.
///
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
/// Holding any other borrow while trying to borrow the backend
/// mutably will panic. Note that EGL will borrow the display
/// mutably during surface creation.
pub fn borrow_mut(&self) -> RefMut<'_, N> {
self.native.borrow_mut()
}
}
#[cfg(feature = "use_system_lib")]
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGLDisplay<B, N> {
/// Binds this EGL display to the given Wayland display. /// Binds this EGL display to the given Wayland display.
/// ///
/// This will allow clients to utilize EGL to create hardware-accelerated /// This will allow clients to utilize EGL to create hardware-accelerated
@ -434,7 +443,8 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGL
/// ///
/// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound) /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound)
/// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time. /// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time.
fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, Error> { #[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
pub fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, Error> {
if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") { if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") {
return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
} }
@ -453,8 +463,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGL
pub struct EGLBufferReader { pub struct EGLBufferReader {
display: Arc<EGLDisplayHandle>, display: Arc<EGLDisplayHandle>,
wayland: *mut wl_display, wayland: *mut wl_display,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
} }
// Gles2 does not implement debug, so we have to impl Debug manually // Gles2 does not implement debug, so we have to impl Debug manually
@ -471,14 +479,10 @@ impl fmt::Debug for EGLBufferReader {
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
impl EGLBufferReader { impl EGLBufferReader {
fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self { fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {
#[cfg(feature = "renderer_gl")]
let gl = gl_ffi::Gles2::load_with(|s| get_proc_address(s) as *const _);
Self { Self {
display, display,
wayland, wayland,
#[cfg(feature = "renderer_gl")]
gl,
} }
} }
@ -576,8 +580,6 @@ impl EGLBufferReader {
y_inverted: inverted != 0, y_inverted: inverted != 0,
format, format,
images, images,
#[cfg(feature = "renderer_gl")]
gl: self.gl.clone(),
}) })
} }
@ -625,3 +627,24 @@ impl Drop for EGLBufferReader {
} }
} }
} }
/// Describes the pixel format of a framebuffer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormat {
/// is the format hardware accelerated
pub hardware_accelerated: bool,
/// number of bits used for colors
pub color_bits: u8,
/// number of bits used for alpha channel
pub alpha_bits: u8,
/// number of bits used for depth channel
pub depth_bits: u8,
/// number of bits used for stencil buffer
pub stencil_bits: u8,
/// is stereoscopy enabled
pub stereoscopy: bool,
/// number of samples used for multisampling if enabled
pub multisampling: Option<u16>,
/// is srgb enabled
pub srgb: bool,
}

View File

@ -198,66 +198,4 @@ pub mod egl {
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL: // Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
pub const EGL_TEXTURE_FORMAT: i32 = 0x3080; pub const EGL_TEXTURE_FORMAT: i32 = 0x3080;
pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB; pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB;
/// nVidia support needs some implemented but only proposed egl extensions...
/// Therefor gl_generator cannot generate them and we need some constants...
/// And a function...
#[cfg(feature = "backend_drm_eglstream")]
pub const CONSUMER_AUTO_ACQUIRE_EXT: i32 = 0x332B;
#[cfg(feature = "backend_drm_eglstream")]
pub const DRM_FLIP_EVENT_DATA_NV: i32 = 0x333E;
#[cfg(feature = "backend_drm_eglstream")]
pub const CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR: i32 = 0x321E;
#[cfg(feature = "backend_drm_eglstream")]
pub const RESOURCE_BUSY_EXT: u32 = 0x3353;
#[cfg(feature = "backend_drm_eglstream")]
#[allow(non_snake_case, unused_variables, dead_code)]
#[inline]
pub unsafe fn StreamConsumerAcquireAttribNV(
dpy: types::EGLDisplay,
stream: types::EGLStreamKHR,
attrib_list: *const types::EGLAttrib,
) -> types::EGLBoolean {
__gl_imports::mem::transmute::<
_,
extern "system" fn(
types::EGLDisplay,
types::EGLStreamKHR,
*const types::EGLAttrib,
) -> types::EGLBoolean,
>(nvidia_storage::StreamConsumerAcquireAttribNV.f)(dpy, stream, attrib_list)
}
#[cfg(feature = "backend_drm_eglstream")]
mod nvidia_storage {
use super::{FnPtr, __gl_imports::raw};
pub static mut StreamConsumerAcquireAttribNV: FnPtr = FnPtr {
f: super::missing_fn_panic as *const raw::c_void,
is_loaded: false,
};
}
#[cfg(feature = "backend_drm_eglstream")]
#[allow(non_snake_case)]
pub mod StreamConsumerAcquireAttribNV {
use super::{FnPtr, __gl_imports::raw, metaloadfn, nvidia_storage};
#[inline]
#[allow(dead_code)]
pub fn is_loaded() -> bool {
unsafe { nvidia_storage::StreamConsumerAcquireAttribNV.is_loaded }
}
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F)
where
F: FnMut(&str) -> *const raw::c_void,
{
unsafe {
nvidia_storage::StreamConsumerAcquireAttribNV =
FnPtr::new(metaloadfn(&mut loadfn, "eglStreamConsumerAcquireAttribNV", &[]))
}
}
}
} }

View File

@ -18,20 +18,20 @@
//! You may then use the resulting [`EGLDisplay`](::backend::egl::EGLDisplay) to receive [`EGLImages`](::backend::egl::EGLImages) //! You may then use the resulting [`EGLDisplay`](::backend::egl::EGLDisplay) to receive [`EGLImages`](::backend::egl::EGLImages)
//! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering. //! of an EGL-based [`WlBuffer`](wayland_server::protocol::wl_buffer::WlBuffer) for rendering.
/*
#[cfg(feature = "renderer_gl")] #[cfg(feature = "renderer_gl")]
use crate::backend::graphics::{ use crate::backend::graphics::{
gl::{ffi as gl_ffi, GLGraphicsBackend}, gl::{ffi as gl_ffi, GLGraphicsBackend},
SwapBuffersError as GraphicsSwapBuffersError, SwapBuffersError as GraphicsSwapBuffersError,
}; };
use nix::libc::c_uint; */
use std::fmt; use std::fmt;
#[cfg(feature = "wayland_frontend")]
use wayland_server::Display;
pub mod context; pub mod context;
pub use self::context::EGLContext; pub use self::context::EGLContext;
mod error; mod error;
pub use self::error::*; pub use self::error::*;
use crate::backend::SwapBuffersError as GraphicsSwapBuffersError;
use nix::libc::c_void; use nix::libc::c_void;
@ -43,11 +43,7 @@ pub mod display;
pub mod native; pub mod native;
pub mod surface; pub mod surface;
pub use self::surface::EGLSurface; pub use self::surface::EGLSurface;
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::display::EGLBufferReader;
use crate::backend::egl::display::EGLDisplayHandle; use crate::backend::egl::display::EGLDisplayHandle;
#[cfg(feature = "renderer_gl")]
use std::ffi::CStr;
use std::ffi::CString; use std::ffi::CString;
use std::sync::Arc; use std::sync::Arc;
@ -71,7 +67,7 @@ impl ::std::error::Error for EglExtensionNotSupportedError {}
/// Returns the address of an OpenGL function. /// Returns the address of an OpenGL function.
/// ///
/// Result is independent of displays and does not guarantee an extension is actually supported at runtime. /// Result is independent of displays and does not guarantee an extension is actually supported at runtime.
pub fn get_proc_address(symbol: &str) -> *const c_void { pub unsafe fn get_proc_address(symbol: &str) -> *const c_void {
unsafe { unsafe {
let addr = CString::new(symbol.as_bytes()).unwrap(); let addr = CString::new(symbol.as_bytes()).unwrap();
let addr = addr.as_ptr(); let addr = addr.as_ptr();
@ -111,45 +107,29 @@ impl fmt::Debug for BufferAccessError {
} }
} }
/// Error that can occur when creating a surface.
#[derive(Debug, thiserror::Error)]
pub enum SurfaceCreationError<E: std::error::Error + 'static> {
/// Native Surface creation failed
#[error("Surface creation failed. Err: {0:}")]
NativeSurfaceCreationFailed(#[source] E),
/// EGL surface creation failed
#[error("EGL surface creation failed. Err: {0:}")]
EGLSurfaceCreationFailed(#[source] EGLError),
}
/// Error that can happen when swapping buffers. /// Error that can happen when swapping buffers.
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum SwapBuffersError<E: std::error::Error + 'static> { pub enum SwapBuffersError {
/// Error of the underlying native surface
#[error("Underlying error: {0:?}")]
Underlying(#[source] E),
/// EGL error during `eglSwapBuffers` /// EGL error during `eglSwapBuffers`
#[error("{0:}")] #[error("{0:}")]
EGLSwapBuffers(#[source] EGLError), EGLSwapBuffers(#[source] EGLError),
/// EGL error during `eglCreateWindowSurface` /// EGL error during surface creation
#[error("{0:}")] #[error("{0:}")]
EGLCreateWindowSurface(#[source] EGLError), EGLCreateSurface(#[source] EGLError),
} }
impl<E: std::error::Error> std::convert::TryFrom<SwapBuffersError<E>> for GraphicsSwapBuffersError { impl std::convert::From<SwapBuffersError> for GraphicsSwapBuffersError {
type Error = E; fn from(value: SwapBuffersError) -> Self {
fn try_from(value: SwapBuffersError<E>) -> Result<Self, Self::Error> {
match value { match value {
// bad surface is answered with a surface recreation in `swap_buffers` // bad surface is answered with a surface recreation in `swap_buffers`
x @ SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface) => { x @ SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface) => {
Ok(GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))) GraphicsSwapBuffersError::TemporaryFailure(Box::new(x))
} }
// the rest is either never happening or are unrecoverable // the rest is either never happening or are unrecoverable
x @ SwapBuffersError::EGLSwapBuffers(_) => Ok(GraphicsSwapBuffersError::ContextLost(Box::new(x))), x @ SwapBuffersError::EGLSwapBuffers(_) => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
x @ SwapBuffersError::EGLCreateWindowSurface(_) => { x @ SwapBuffersError::EGLCreateSurface(_) => {
Ok(GraphicsSwapBuffersError::ContextLost(Box::new(x))) GraphicsSwapBuffersError::ContextLost(Box::new(x))
} }
SwapBuffersError::Underlying(e) => Err(e),
} }
} }
} }
@ -265,8 +245,6 @@ pub struct EGLImages {
/// Format of these images /// Format of these images
pub format: Format, pub format: Format,
images: Vec<EGLImage>, images: Vec<EGLImage>,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
} }
// Gles2 does not implement debug, so we have to impl Debug manually // Gles2 does not implement debug, so we have to impl Debug manually
@ -291,6 +269,7 @@ impl EGLImages {
self.format.num_planes() self.format.num_planes()
} }
/*
/// Bind plane to an OpenGL texture id /// Bind plane to an OpenGL texture id
/// ///
/// This does only temporarily modify the OpenGL state any changes are reverted before returning. /// This does only temporarily modify the OpenGL state any changes are reverted before returning.
@ -342,6 +321,7 @@ impl EGLImages {
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32); self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
res res
} }
*/
} }
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
@ -355,22 +335,3 @@ impl Drop for EGLImages {
} }
} }
} }
/// Trait any backend type may implement that allows binding a [`Display`](wayland_server::Display)
/// to create an [`EGLBufferReader`](display::EGLBufferReader) for EGL-based [`WlBuffer`]s.
#[cfg(feature = "use_system_lib")]
pub trait EGLGraphicsBackend {
/// Binds this EGL context to the given Wayland display.
///
/// This will allow clients to utilize EGL to create hardware-accelerated
/// surfaces. The server will need to be able to handle EGL-[`WlBuffer`]s.
///
/// ## Errors
///
/// This might return [`EglExtensionNotSupported`](ErrorKind::EglExtensionNotSupported)
/// if binding is not supported by the EGL implementation.
///
/// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound)
/// if called for the same [`Display`] multiple times, as only one context may be bound at any given time.
fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, Error>;
}

View File

@ -1,10 +1,12 @@
//! Type safe native types for safe context/surface creation //! Type safe native types for safe context/surface creation
use super::{ use super::{
display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error, SurfaceCreationError, SwapBuffersError, display::EGLDisplayHandle, ffi, wrap_egl_call, SwapBuffersError,
}; };
use nix::libc::{c_int, c_void}; use nix::libc::{c_int, c_void};
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "backend_gbm")]
use std::os::unix::io::AsRawFd;
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
use wayland_egl as wegl; use wayland_egl as wegl;
@ -13,186 +15,47 @@ use winit::platform::unix::WindowExtUnix;
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
use winit::window::Window as WinitWindow; use winit::window::Window as WinitWindow;
/// Trait for typed backend variants (X11/Wayland/GBM) #[cfg(feature = "backend_gbm")]
pub trait Backend { use gbm::{AsRaw, Device as GbmDevice};
/// Surface type created by this backend
type Surface: NativeSurface<Error = Self::Error>;
/// Error type thrown by the surface creation in case of failure.
type Error: ::std::error::Error + Send + 'static;
/// Return an [`EGLDisplay`](ffi::egl::types::EGLDisplay) based on this backend pub trait EGLNativeDisplay: Send {
/// fn required_extensions(&self) -> &'static [&'static str];
/// # Safety fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>);
///
/// The returned [`EGLDisplay`](ffi::egl::types::EGLDisplay) needs to be a valid pointer for EGL,
/// but there is no way to test that.
unsafe fn get_display<F: Fn(&str) -> bool>(
display: ffi::NativeDisplayType,
attribs: &[ffi::EGLint],
has_dp_extension: F,
log: ::slog::Logger,
) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
}
#[cfg(feature = "backend_winit")]
#[derive(Debug)]
/// Wayland backend type
pub enum Wayland {}
#[cfg(feature = "backend_winit")]
impl Backend for Wayland {
type Surface = wegl::WlEglSurface;
type Error = Error;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType,
attribs: &[ffi::EGLint],
has_dp_extension: F,
log: ::slog::Logger,
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where
F: Fn(&str) -> bool,
{
if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
wrap_egl_call(|| {
ffi::egl::GetPlatformDisplay(
ffi::egl::PLATFORM_WAYLAND_KHR,
display as *mut _,
attribs.as_ptr(),
)
})
} else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland");
wrap_egl_call(|| {
ffi::egl::GetPlatformDisplayEXT(
ffi::egl::PLATFORM_WAYLAND_EXT,
display as *mut _,
attribs.as_ptr(),
)
})
} else {
trace!(log, "Default EGL Display Initialization via GetDisplay");
wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
}
}
}
#[cfg(feature = "backend_winit")]
#[derive(Debug)]
/// Typed Xlib window for the `X11` backend
pub struct XlibWindow(u64);
#[cfg(feature = "backend_winit")]
/// X11 backend type
#[derive(Debug)]
pub enum X11 {}
#[cfg(feature = "backend_winit")]
impl Backend for X11 {
type Surface = XlibWindow;
type Error = Error;
unsafe fn get_display<F>(
display: ffi::NativeDisplayType,
attribs: &[ffi::EGLint],
has_dp_extension: F,
log: ::slog::Logger,
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where
F: Fn(&str) -> bool,
{
if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
wrap_egl_call(|| {
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, attribs.as_ptr())
})
} else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
wrap_egl_call(|| {
ffi::egl::GetPlatformDisplayEXT(
ffi::egl::PLATFORM_X11_EXT,
display as *mut _,
attribs.as_ptr(),
)
})
} else {
trace!(log, "Default EGL Display Initialization via GetDisplay");
wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
}
}
}
/// Trait for types returning Surfaces which can be used to initialize [`EGLSurface`](super::EGLSurface)s
///
/// ## Unsafety
///
/// The returned [`NativeDisplayType`](super::ffi::NativeDisplayType) must be valid for EGL and there is no way to test that.
pub unsafe trait NativeDisplay<B: Backend> {
/// Arguments used to surface creation.
type Arguments;
/// Because one type might implement multiple [`Backend`]s this function must be called to check
/// if the expected [`Backend`] is used at runtime.
fn is_backend(&self) -> bool;
/// Return a raw pointer EGL will accept for context creation.
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
/// Return attributes that might be used by `B::get_display`
///
/// Default implementation returns an empty list
fn attributes(&self) -> Vec<ffi::EGLint> {
vec![ffi::egl::NONE as 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
} }
/// Create a surface
fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, B::Error>;
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_gbm")]
unsafe impl NativeDisplay<X11> for WinitWindow { impl<A: AsRawFd + Send + 'static> EGLNativeDisplay for GbmDevice<A> {
type Arguments = (); fn required_extensions(&self) -> &'static [&'static str] {
&["EGL_MESA_platform_gbm"]
fn is_backend(&self) -> bool {
self.xlib_display().is_some()
} }
fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>) {
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> { (ffi::egl::PLATFORM_GBM_MESA, self.as_raw() as *mut _, vec![ffi::egl::NONE as ffi::EGLint])
self.xlib_display()
.map(|ptr| ptr as *const _)
.ok_or(Error::NonMatchingBackend("X11"))
}
fn create_surface(&mut self, _args: ()) -> Result<XlibWindow, Error> {
self.xlib_window()
.map(XlibWindow)
.ok_or(Error::NonMatchingBackend("X11"))
} }
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<Wayland> for WinitWindow { impl EGLNativeDisplay for WinitWindow {
type Arguments = (); fn required_extensions(&self) -> &'static [&'static str] {
if self.wayland_display().is_some() {
fn is_backend(&self) -> bool { &["EGL_EXT_platform_wayland"]
self.wayland_display().is_some() } else if self.xlib_display().is_some() {
} &["EGL_EXT_platform_x11"]
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
self.wayland_display()
.map(|ptr| ptr as *const _)
.ok_or(Error::NonMatchingBackend("Wayland"))
}
fn create_surface(&mut self, _args: ()) -> Result<wegl::WlEglSurface, Error> {
if let Some(surface) = self.wayland_surface() {
let size = self.inner_size();
Ok(unsafe {
wegl::WlEglSurface::new_from_raw(surface as *mut _, size.width as i32, size.height as i32)
})
} else { } else {
Err(Error::NonMatchingBackend("Wayland")) 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() {
(ffi::egl::PLATFORM_WAYLAND_EXT, display as *mut _, vec![ffi::egl::NONE as ffi::EGLint])
} else if let Some(display) = self.xlib_display() {
(ffi::egl::PLATFORM_X11_EXT, display as *mut _, vec![ffi::egl::NONE as ffi::EGLint])
} else {
unreachable!("No backends for winit other then Wayland and X11 are supported")
} }
} }
} }
@ -203,22 +66,19 @@ unsafe impl NativeDisplay<Wayland> for WinitWindow {
/// ///
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL /// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
/// and there is no way to test that. /// and there is no way to test that.
pub unsafe trait NativeSurface { pub unsafe trait EGLNativeSurface: Send + Sync {
/// Error type thrown by the surface creation in case of failure. /// Error type thrown by the surface creation in case of failure.
type Error: ::std::error::Error + Send + 'static;
/// Create an EGLSurface from the internal native type. /// Create an EGLSurface from the internal native type.
/// ///
/// Must be able to deal with re-creation of existing resources, /// Must be able to deal with re-creation of existing resources,
/// if `needs_recreation` can return `true`. /// if `needs_recreation` can return `true`.
/// ///
/// # Safety fn create(
/// This is usually an unsafe operation returning a raw pointer.
unsafe fn create(
&self, &self,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
surface_attributes: &[c_int], surface_attributes: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Self::Error>>; ) -> Result<*const c_void, super::EGLError>;
/// Will be called to check if any internal resources will need /// Will be called to check if any internal resources will need
/// to be recreated. Old resources must be used until `create` /// to be recreated. Old resources must be used until `create`
@ -230,6 +90,19 @@ pub unsafe trait NativeSurface {
false false
} }
/// If the surface supports resizing you may implement and use this function.
///
/// The two first arguments (width, height) are the new size of the surface,
/// the two others (dx, dy) represent the displacement of the top-left corner of the surface.
/// It allows you to control the direction of the resizing if necessary.
///
/// Implementations may ignore the dx and dy arguments.
///
/// Returns true if the resize was successful.
fn resize(&self, _width: i32, _height: i32, _dx: i32, _dy: i32) -> bool {
false
}
/// Adds additional semantics when calling /// Adds additional semantics when calling
/// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers) /// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
/// ///
@ -238,7 +111,7 @@ pub unsafe trait NativeSurface {
&self, &self,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface, surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Self::Error>> { ) -> Result<(), SwapBuffersError> {
wrap_egl_call(|| unsafe { wrap_egl_call(|| unsafe {
ffi::egl::SwapBuffers(***display, surface as *const _); ffi::egl::SwapBuffers(***display, surface as *const _);
}) })
@ -247,45 +120,49 @@ pub unsafe trait NativeSurface {
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for XlibWindow { /// Typed Xlib window for the `X11` backend
type Error = Error; pub struct XlibWindow(pub u64);
unsafe fn create( #[cfg(feature = "backend_winit")]
unsafe impl EGLNativeSurface for XlibWindow {
fn create(
&self, &self,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
surface_attributes: &[c_int], surface_attributes: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Error>> { ) -> Result<*const c_void, super::EGLError> {
wrap_egl_call(|| { wrap_egl_call(|| unsafe {
ffi::egl::CreateWindowSurface( let mut id = self.0;
ffi::egl::CreatePlatformWindowSurfaceEXT(
display.handle, display.handle,
config_id, config_id,
self.0 as *const _, (&mut id) as *mut u64 as *mut _,
surface_attributes.as_ptr(), surface_attributes.as_ptr(),
) )
}) })
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
} }
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface { unsafe impl EGLNativeSurface for wegl::WlEglSurface {
type Error = Error; fn create(
unsafe fn create(
&self, &self,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
surface_attributes: &[c_int], surface_attributes: &[c_int],
) -> Result<*const c_void, SurfaceCreationError<Error>> { ) -> Result<*const c_void, super::EGLError> {
wrap_egl_call(|| { wrap_egl_call(|| unsafe {
ffi::egl::CreateWindowSurface( ffi::egl::CreatePlatformWindowSurfaceEXT(
display.handle, display.handle,
config_id, config_id,
self.ptr() as *const _, self.ptr() as *mut _,
surface_attributes.as_ptr(), surface_attributes.as_ptr(),
) )
}) })
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed) }
fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
wegl::WlEglSurface::resize(self, width, height, dx, dy);
true
} }
} }

View File

@ -1,20 +1,23 @@
//! EGL surface related structs //! EGL surface related structs
use super::{ffi, native, EGLError, SurfaceCreationError, SwapBuffersError};
use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::PixelFormat;
use nix::libc::c_int;
use std::ops::{Deref, DerefMut};
use std::sync::{ use std::sync::{
atomic::{AtomicPtr, Ordering}, atomic::{AtomicPtr, Ordering},
Arc, Arc,
}; };
use nix::libc::c_int;
use crate::backend::egl::{
display::{EGLDisplay, EGLDisplayHandle, PixelFormat},
native::EGLNativeSurface,
ffi, EGLError, SwapBuffersError
};
/// EGL surface of a given EGL context for rendering /// EGL surface of a given EGL context for rendering
#[derive(Debug)] pub struct EGLSurface {
pub struct EGLSurface<N: native::NativeSurface> {
pub(crate) display: Arc<EGLDisplayHandle>, pub(crate) display: Arc<EGLDisplayHandle>,
native: N, native: Box<dyn EGLNativeSurface + Send + 'static>,
pub(crate) surface: AtomicPtr<nix::libc::c_void>, pub(crate) surface: AtomicPtr<nix::libc::c_void>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat, pixel_format: PixelFormat,
@ -23,32 +26,19 @@ pub struct EGLSurface<N: native::NativeSurface> {
} }
// safe because EGLConfig can be moved between threads // safe because EGLConfig can be moved between threads
// and the other types are thread-safe // and the other types are thread-safe
unsafe impl<N: native::NativeSurface + Send> Send for EGLSurface<N> {} unsafe impl Send for EGLSurface {}
unsafe impl<N: native::NativeSurface + Send + Sync> Sync for EGLSurface<N> {}
impl<N: native::NativeSurface> Deref for EGLSurface<N> { impl EGLSurface {
type Target = N; pub fn new<N, L>(
fn deref(&self) -> &N { display: &EGLDisplay,
&self.native
}
}
impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
fn deref_mut(&mut self) -> &mut N {
&mut self.native
}
}
impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<L>(
display: Arc<EGLDisplayHandle>,
pixel_format: PixelFormat, pixel_format: PixelFormat,
double_buffered: Option<bool>, double_buffered: Option<bool>,
config: ffi::egl::types::EGLConfig, config: ffi::egl::types::EGLConfig,
native: N, native: N,
log: L, log: L,
) -> Result<EGLSurface<N>, SurfaceCreationError<N::Error>> ) -> Result<EGLSurface, EGLError>
where where
N: EGLNativeSurface + Send + 'static,
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "renderer_egl")); let log = crate::slog_or_fallback(log.into()).new(o!("smithay_module" => "renderer_egl"));
@ -74,17 +64,15 @@ impl<N: native::NativeSurface> EGLSurface<N> {
out out
}; };
let surface = unsafe { native.create(&display, config, &surface_attributes)? }; let surface = native.create(&display.display, config, &surface_attributes)?;
if surface == ffi::egl::NO_SURFACE { if surface == ffi::egl::NO_SURFACE {
return Err(SurfaceCreationError::EGLSurfaceCreationFailed( return Err(EGLError::BadSurface);
EGLError::BadSurface,
));
} }
Ok(EGLSurface { Ok(EGLSurface {
display, display: display.display.clone(),
native, native: Box::new(native),
surface: AtomicPtr::new(surface as *mut _), surface: AtomicPtr::new(surface as *mut _),
config_id: config, config_id: config,
pixel_format, pixel_format,
@ -94,7 +82,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
} }
/// Swaps buffers at the end of a frame. /// Swaps buffers at the end of a frame.
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> { pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
let surface = self.surface.load(Ordering::SeqCst); let surface = self.surface.load(Ordering::SeqCst);
let result = if !surface.is_null() { let result = if !surface.is_null() {
@ -115,14 +103,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
unsafe { unsafe {
self.native self.native
.create(&self.display, self.config_id, &self.surface_attributes) .create(&self.display, self.config_id, &self.surface_attributes)
.map_err(|err| match err { .map_err(SwapBuffersError::EGLCreateSurface)? as *mut _
SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
SwapBuffersError::EGLCreateWindowSurface(err)
}
SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
SwapBuffersError::Underlying(err)
}
})? as *mut _
}, },
Ordering::SeqCst, Ordering::SeqCst,
); );
@ -151,17 +132,30 @@ impl<N: native::NativeSurface> EGLSurface<N> {
} }
/// Returns the egl config for this context /// Returns the egl config for this context
pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { pub fn config_id(&self) -> ffi::egl::types::EGLConfig {
self.config_id self.config_id
} }
/// Returns the pixel format of the main framebuffer of the context. /// Returns the pixel format of the main framebuffer of the context.
pub fn get_pixel_format(&self) -> PixelFormat { pub fn pixel_format(&self) -> PixelFormat {
self.pixel_format self.pixel_format
} }
/// Tries to resize the underlying native surface.
///
/// The two first arguments (width, height) are the new size of the surface,
/// the two others (dx, dy) represent the displacement of the top-left corner of the surface.
/// It allows you to control the direction of the resizing if necessary.
///
/// Implementations may ignore the dx and dy arguments.
///
/// Returns true if the resize was successful.
pub fn resize(&self, width: i32, height: i32, dx: i32, dy: i32) -> bool {
self.native.resize(width, height, dx, dy)
}
} }
impl<N: native::NativeSurface> Drop for EGLSurface<N> { impl Drop for EGLSurface {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _); ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);