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:
parent
d606165088
commit
d99108a8e6
|
@ -1,96 +1,106 @@
|
|||
//! 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::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
|
||||
#[derive(Debug)]
|
||||
pub struct EGLContext {
|
||||
context: ffi::egl::types::EGLContext,
|
||||
display: Arc<EGLDisplayHandle>,
|
||||
pub(crate) display: EGLDisplay,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
pixel_format: PixelFormat,
|
||||
pixel_format: Option<PixelFormat>,
|
||||
}
|
||||
// EGLContexts can be moved between threads safely
|
||||
unsafe impl Send for EGLContext {}
|
||||
unsafe impl Sync for 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)
|
||||
pub(crate) fn new<B, N, L>(
|
||||
display: &EGLDisplay<B, N>,
|
||||
mut attributes: GlAttributes,
|
||||
pub fn new_with_config<L>(
|
||||
display: &EGLDisplay,
|
||||
attributes: GlAttributes,
|
||||
reqs: PixelFormatRequirements,
|
||||
log: L,
|
||||
) -> Result<EGLContext, Error>
|
||||
where
|
||||
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,
|
||||
// fallback to 2.0 otherwise
|
||||
let version = match attributes.version {
|
||||
Some((3, x)) => (3, x),
|
||||
Some((2, x)) => (2, x),
|
||||
fn new_internal<L>(
|
||||
display: &EGLDisplay,
|
||||
config: Option<(GlAttributes, PixelFormatRequirements)>,
|
||||
log: L,
|
||||
) -> 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 => {
|
||||
debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
|
||||
attributes.version = Some((3, 0));
|
||||
match EGLContext::new(display, attributes, reqs, log.clone()) {
|
||||
Ok(x) => return Ok(x),
|
||||
Err(err) => {
|
||||
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());
|
||||
}
|
||||
if !display.extensions.iter().any(|x| x == "EGL_KHR_no_config_context") &&
|
||||
!display.extensions.iter().any(|x| x == "EGL_MESA_configless_context") &&
|
||||
!display.extensions.iter().any(|x| x == "EGL_KHR_surfaceless_context")
|
||||
{
|
||||
return Err(Error::EglExtensionNotSupported(&["EGL_KHR_no_config_context", "EGL_MESA_configless_context", "EGL_KHR_surfaceless_context"]));
|
||||
}
|
||||
}
|
||||
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));
|
||||
(None, ffi::egl::NO_CONFIG_KHR)
|
||||
}
|
||||
};
|
||||
|
||||
let (pixel_format, config_id) = display.choose_config(attributes, reqs)?;
|
||||
|
||||
let mut context_attributes = Vec::with_capacity(10);
|
||||
|
||||
if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
|
||||
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
|
||||
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 let Some((attributes, _)) = config {
|
||||
let version = attributes.version;
|
||||
|
||||
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);
|
||||
if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") {
|
||||
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
} else {
|
||||
trace!(log, "Setting CONTEXT_CLIENT_VERSION to 2");
|
||||
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);
|
||||
|
@ -111,7 +121,7 @@ impl EGLContext {
|
|||
|
||||
Ok(EGLContext {
|
||||
context,
|
||||
display: display.display.clone(),
|
||||
display: display.clone(),
|
||||
config_id,
|
||||
pixel_format,
|
||||
})
|
||||
|
@ -124,12 +134,10 @@ impl EGLContext {
|
|||
///
|
||||
/// This function is marked unsafe, because the context cannot be made current
|
||||
/// on multiple threads.
|
||||
pub unsafe fn make_current_with_surface<N>(&self, surface: &EGLSurface<N>) -> Result<(), MakeCurrentError>
|
||||
where
|
||||
N: NativeSurface,
|
||||
pub unsafe fn make_current_with_surface(&self, surface: &EGLSurface) -> Result<(), MakeCurrentError>
|
||||
{
|
||||
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_err(Into::into)
|
||||
}
|
||||
|
@ -143,7 +151,7 @@ impl EGLContext {
|
|||
pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::MakeCurrent(
|
||||
**self.display,
|
||||
**self.display.display,
|
||||
ffi::egl::NO_SURFACE,
|
||||
ffi::egl::NO_SURFACE,
|
||||
self.context,
|
||||
|
@ -159,12 +167,12 @@ impl EGLContext {
|
|||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
|
@ -175,7 +183,7 @@ impl EGLContext {
|
|||
if self.is_current() {
|
||||
wrap_egl_call(|| unsafe {
|
||||
ffi::egl::MakeCurrent(
|
||||
**self.display,
|
||||
**self.display.display,
|
||||
ffi::egl::NO_SURFACE,
|
||||
ffi::egl::NO_SURFACE,
|
||||
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
|
||||
// ignore failures at this point
|
||||
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 {
|
||||
/// 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.
|
||||
/// `None` means "don't care" (minimum will be 2.0).
|
||||
pub version: Option<(u8, u8)>,
|
||||
/// `(3, 0)` will request a OpenGL ES 3.0 context for example.
|
||||
/// `(2, 0)` is the minimum.
|
||||
pub version: (u8, u8),
|
||||
/// OpenGL profile to use
|
||||
pub profile: Option<GlProfile>,
|
||||
/// Whether to enable the debug flag of the context.
|
||||
|
|
|
@ -1,31 +1,24 @@
|
|||
//! 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::ffi::CStr;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ops::Deref;
|
||||
|
||||
use nix::libc::c_int;
|
||||
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
|
||||
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
|
||||
#[cfg(feature = "use_system_lib")]
|
||||
use wayland_sys::server::wl_display;
|
||||
|
||||
use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements};
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use crate::backend::graphics::gl::ffi as gl_ffi;
|
||||
use crate::backend::graphics::PixelFormat;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use crate::backend::allocator::{Buffer, dmabuf::Dmabuf};
|
||||
use crate::backend::egl::{
|
||||
ffi::egl::types::EGLImage,
|
||||
ffi, wrap_egl_call, EGLError, Error,
|
||||
context::{GlAttributes, PixelFormatRequirements},
|
||||
native::{EGLNativeDisplay},
|
||||
BufferAccessError, EGLImages, Format,
|
||||
};
|
||||
|
||||
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
|
||||
/// once all resources bound to it have been dropped.
|
||||
|
@ -56,55 +49,54 @@ impl Drop for EGLDisplayHandle {
|
|||
}
|
||||
|
||||
/// [`EGLDisplay`] represents an initialised EGL environment
|
||||
#[derive(Debug)]
|
||||
pub struct EGLDisplay<B: native::Backend, N: native::NativeDisplay<B>> {
|
||||
native: RefCell<N>,
|
||||
#[derive(Clone)]
|
||||
pub struct EGLDisplay {
|
||||
pub(crate) display: Arc<EGLDisplayHandle>,
|
||||
pub(crate) egl_version: (i32, i32),
|
||||
pub(crate) extensions: Vec<String>,
|
||||
surface_type: ffi::EGLint,
|
||||
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)
|
||||
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
|
||||
N: EGLNativeDisplay + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "renderer_egl"));
|
||||
let ptr = native.ptr()?;
|
||||
let egl_attribs = native.attributes();
|
||||
|
||||
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)?;
|
||||
.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() {
|
||||
vec![]
|
||||
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, "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
|
||||
let display = unsafe {
|
||||
B::get_display(
|
||||
ptr,
|
||||
&egl_attribs,
|
||||
|e: &str| dp_extensions.iter().any(|s| s == e),
|
||||
log.clone(),
|
||||
)
|
||||
.map_err(Error::DisplayCreationError)?
|
||||
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);
|
||||
|
@ -143,7 +135,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
} else {
|
||||
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?)
|
||||
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)))?;
|
||||
|
||||
Ok(EGLDisplay {
|
||||
native: RefCell::new(native),
|
||||
display: Arc::new(EGLDisplayHandle { handle: display }),
|
||||
surface_type: native.surface_type(),
|
||||
egl_version,
|
||||
extensions,
|
||||
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> {
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
let surface_type = self.native.borrow().surface_type();
|
||||
|
||||
if self.egl_version >= (1, 2) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
||||
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
||||
out.push(surface_type);
|
||||
out.push(self.surface_type);
|
||||
|
||||
match attributes.version {
|
||||
Some((3, _)) => {
|
||||
(3, _) => {
|
||||
if self.egl_version < (1, 3) {
|
||||
error!(
|
||||
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::OPENGL_ES3_BIT as c_int);
|
||||
}
|
||||
Some((2, _)) => {
|
||||
(2, _) => {
|
||||
if self.egl_version < (1, 3) {
|
||||
error!(
|
||||
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::OPENGL_ES2_BIT as c_int);
|
||||
}
|
||||
Some(ver) => {
|
||||
ver => {
|
||||
return Err(Error::OpenGlVersionNotSupported(ver));
|
||||
}
|
||||
None => {
|
||||
return Err(Error::OpenGlVersionNotSupported((0, 0)));
|
||||
}
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
/// 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
|
||||
pub fn get_egl_version(&self) -> (i32, i32) {
|
||||
self.egl_version
|
||||
|
@ -400,28 +347,90 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
self.extensions.clone()
|
||||
}
|
||||
|
||||
/// Borrow the underlying native display.
|
||||
///
|
||||
/// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell).
|
||||
/// Multiple read-only borrows are possible. Borrowing the
|
||||
/// backend while there is a mutable reference will panic.
|
||||
pub fn borrow(&self) -> Ref<'_, N> {
|
||||
self.native.borrow()
|
||||
/// Imports a dmabuf as an eglimage
|
||||
pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {
|
||||
if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base") &&
|
||||
!self.extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import")
|
||||
{
|
||||
return Err(Error::EglExtensionNotSupported(&["EGL_KHR_image_base", "EGL_EXT_image_dma_buf_import"]));
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// 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)
|
||||
/// 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") {
|
||||
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 {
|
||||
display: Arc<EGLDisplayHandle>,
|
||||
wayland: *mut wl_display,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl: gl_ffi::Gles2,
|
||||
}
|
||||
|
||||
// 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")]
|
||||
impl EGLBufferReader {
|
||||
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 {
|
||||
display,
|
||||
wayland,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -576,8 +580,6 @@ impl EGLBufferReader {
|
|||
y_inverted: inverted != 0,
|
||||
format,
|
||||
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,
|
||||
}
|
|
@ -198,66 +198,4 @@ pub mod egl {
|
|||
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
|
||||
pub const EGL_TEXTURE_FORMAT: i32 = 0x3080;
|
||||
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", &[]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,20 +18,20 @@
|
|||
//! 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.
|
||||
|
||||
/*
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use crate::backend::graphics::{
|
||||
gl::{ffi as gl_ffi, GLGraphicsBackend},
|
||||
SwapBuffersError as GraphicsSwapBuffersError,
|
||||
};
|
||||
use nix::libc::c_uint;
|
||||
*/
|
||||
use std::fmt;
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use wayland_server::Display;
|
||||
|
||||
pub mod context;
|
||||
pub use self::context::EGLContext;
|
||||
mod error;
|
||||
pub use self::error::*;
|
||||
use crate::backend::SwapBuffersError as GraphicsSwapBuffersError;
|
||||
|
||||
use nix::libc::c_void;
|
||||
|
||||
|
@ -43,11 +43,7 @@ pub mod display;
|
|||
pub mod native;
|
||||
pub mod surface;
|
||||
pub use self::surface::EGLSurface;
|
||||
#[cfg(feature = "use_system_lib")]
|
||||
use crate::backend::egl::display::EGLBufferReader;
|
||||
use crate::backend::egl::display::EGLDisplayHandle;
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
use std::ffi::CStr;
|
||||
use std::ffi::CString;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -71,7 +67,7 @@ impl ::std::error::Error for EglExtensionNotSupportedError {}
|
|||
/// Returns the address of an OpenGL function.
|
||||
///
|
||||
/// 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 {
|
||||
let addr = CString::new(symbol.as_bytes()).unwrap();
|
||||
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.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum SwapBuffersError<E: std::error::Error + 'static> {
|
||||
/// Error of the underlying native surface
|
||||
#[error("Underlying error: {0:?}")]
|
||||
Underlying(#[source] E),
|
||||
pub enum SwapBuffersError {
|
||||
/// EGL error during `eglSwapBuffers`
|
||||
#[error("{0:}")]
|
||||
EGLSwapBuffers(#[source] EGLError),
|
||||
/// EGL error during `eglCreateWindowSurface`
|
||||
/// EGL error during surface creation
|
||||
#[error("{0:}")]
|
||||
EGLCreateWindowSurface(#[source] EGLError),
|
||||
EGLCreateSurface(#[source] EGLError),
|
||||
}
|
||||
|
||||
impl<E: std::error::Error> std::convert::TryFrom<SwapBuffersError<E>> for GraphicsSwapBuffersError {
|
||||
type Error = E;
|
||||
fn try_from(value: SwapBuffersError<E>) -> Result<Self, Self::Error> {
|
||||
impl std::convert::From<SwapBuffersError> for GraphicsSwapBuffersError {
|
||||
fn from(value: SwapBuffersError) -> Self {
|
||||
match value {
|
||||
// bad surface is answered with a surface recreation in `swap_buffers`
|
||||
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
|
||||
x @ SwapBuffersError::EGLSwapBuffers(_) => Ok(GraphicsSwapBuffersError::ContextLost(Box::new(x))),
|
||||
x @ SwapBuffersError::EGLCreateWindowSurface(_) => {
|
||||
Ok(GraphicsSwapBuffersError::ContextLost(Box::new(x)))
|
||||
x @ SwapBuffersError::EGLSwapBuffers(_) => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
|
||||
x @ SwapBuffersError::EGLCreateSurface(_) => {
|
||||
GraphicsSwapBuffersError::ContextLost(Box::new(x))
|
||||
}
|
||||
SwapBuffersError::Underlying(e) => Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -265,8 +245,6 @@ pub struct EGLImages {
|
|||
/// Format of these images
|
||||
pub format: Format,
|
||||
images: Vec<EGLImage>,
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
gl: gl_ffi::Gles2,
|
||||
}
|
||||
|
||||
// Gles2 does not implement debug, so we have to impl Debug manually
|
||||
|
@ -291,6 +269,7 @@ impl EGLImages {
|
|||
self.format.num_planes()
|
||||
}
|
||||
|
||||
/*
|
||||
/// Bind plane to an OpenGL texture id
|
||||
///
|
||||
/// 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);
|
||||
res
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#[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>;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
//! Type safe native types for safe context/surface creation
|
||||
|
||||
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 std::sync::Arc;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
use wayland_egl as wegl;
|
||||
|
@ -13,186 +15,47 @@ use winit::platform::unix::WindowExtUnix;
|
|||
#[cfg(feature = "backend_winit")]
|
||||
use winit::window::Window as WinitWindow;
|
||||
|
||||
/// Trait for typed backend variants (X11/Wayland/GBM)
|
||||
pub trait Backend {
|
||||
/// 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;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
use gbm::{AsRaw, Device as GbmDevice};
|
||||
|
||||
/// Return an [`EGLDisplay`](ffi::egl::types::EGLDisplay) based on this backend
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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]
|
||||
}
|
||||
pub trait EGLNativeDisplay: Send {
|
||||
fn required_extensions(&self) -> &'static [&'static str];
|
||||
fn platform_display(&self) -> (ffi::egl::types::EGLenum, *mut c_void, Vec<ffi::EGLint>);
|
||||
/// Type of surfaces created
|
||||
fn surface_type(&self) -> 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")]
|
||||
unsafe impl NativeDisplay<X11> for WinitWindow {
|
||||
type Arguments = ();
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
self.xlib_display().is_some()
|
||||
#[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 ptr(&self) -> Result<ffi::NativeDisplayType, Error> {
|
||||
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"))
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
||||
type Arguments = ();
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
self.wayland_display().is_some()
|
||||
}
|
||||
|
||||
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)
|
||||
})
|
||||
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 {
|
||||
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
|
||||
/// 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.
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
/// Create an EGLSurface from the internal native type.
|
||||
///
|
||||
/// Must be able to deal with re-creation of existing resources,
|
||||
/// if `needs_recreation` can return `true`.
|
||||
///
|
||||
/// # Safety
|
||||
/// This is usually an unsafe operation returning a raw pointer.
|
||||
unsafe fn create(
|
||||
fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
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
|
||||
/// to be recreated. Old resources must be used until `create`
|
||||
|
@ -230,6 +90,19 @@ pub unsafe trait NativeSurface {
|
|||
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
|
||||
/// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
|
||||
///
|
||||
|
@ -238,7 +111,7 @@ pub unsafe trait NativeSurface {
|
|||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Self::Error>> {
|
||||
) -> Result<(), SwapBuffersError> {
|
||||
wrap_egl_call(|| unsafe {
|
||||
ffi::egl::SwapBuffers(***display, surface as *const _);
|
||||
})
|
||||
|
@ -247,45 +120,49 @@ pub unsafe trait NativeSurface {
|
|||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeSurface for XlibWindow {
|
||||
type Error = Error;
|
||||
/// Typed Xlib window for the `X11` backend
|
||||
pub struct XlibWindow(pub u64);
|
||||
|
||||
unsafe fn create(
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl EGLNativeSurface for XlibWindow {
|
||||
fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Error>> {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
) -> Result<*const c_void, super::EGLError> {
|
||||
wrap_egl_call(|| unsafe {
|
||||
let mut id = self.0;
|
||||
ffi::egl::CreatePlatformWindowSurfaceEXT(
|
||||
display.handle,
|
||||
config_id,
|
||||
self.0 as *const _,
|
||||
(&mut id) as *mut u64 as *mut _,
|
||||
surface_attributes.as_ptr(),
|
||||
)
|
||||
})
|
||||
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeSurface for wegl::WlEglSurface {
|
||||
type Error = Error;
|
||||
|
||||
unsafe fn create(
|
||||
unsafe impl EGLNativeSurface for wegl::WlEglSurface {
|
||||
fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Error>> {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
) -> Result<*const c_void, super::EGLError> {
|
||||
wrap_egl_call(|| unsafe {
|
||||
ffi::egl::CreatePlatformWindowSurfaceEXT(
|
||||
display.handle,
|
||||
config_id,
|
||||
self.ptr() as *const _,
|
||||
self.ptr() as *mut _,
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
//! 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::{
|
||||
atomic::{AtomicPtr, Ordering},
|
||||
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
|
||||
#[derive(Debug)]
|
||||
pub struct EGLSurface<N: native::NativeSurface> {
|
||||
pub struct EGLSurface {
|
||||
pub(crate) display: Arc<EGLDisplayHandle>,
|
||||
native: N,
|
||||
native: Box<dyn EGLNativeSurface + Send + 'static>,
|
||||
pub(crate) surface: AtomicPtr<nix::libc::c_void>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
pixel_format: PixelFormat,
|
||||
|
@ -23,32 +26,19 @@ pub struct EGLSurface<N: native::NativeSurface> {
|
|||
}
|
||||
// safe because EGLConfig can be moved between threads
|
||||
// and the other types are thread-safe
|
||||
unsafe impl<N: native::NativeSurface + Send> Send for EGLSurface<N> {}
|
||||
unsafe impl<N: native::NativeSurface + Send + Sync> Sync for EGLSurface<N> {}
|
||||
unsafe impl Send for EGLSurface {}
|
||||
|
||||
impl<N: native::NativeSurface> Deref for EGLSurface<N> {
|
||||
type Target = N;
|
||||
fn deref(&self) -> &N {
|
||||
&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>,
|
||||
impl EGLSurface {
|
||||
pub fn new<N, L>(
|
||||
display: &EGLDisplay,
|
||||
pixel_format: PixelFormat,
|
||||
double_buffered: Option<bool>,
|
||||
config: ffi::egl::types::EGLConfig,
|
||||
native: N,
|
||||
log: L,
|
||||
) -> Result<EGLSurface<N>, SurfaceCreationError<N::Error>>
|
||||
) -> Result<EGLSurface, EGLError>
|
||||
where
|
||||
N: EGLNativeSurface + Send + 'static,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
let surface = unsafe { native.create(&display, config, &surface_attributes)? };
|
||||
let surface = native.create(&display.display, config, &surface_attributes)?;
|
||||
|
||||
if surface == ffi::egl::NO_SURFACE {
|
||||
return Err(SurfaceCreationError::EGLSurfaceCreationFailed(
|
||||
EGLError::BadSurface,
|
||||
));
|
||||
return Err(EGLError::BadSurface);
|
||||
}
|
||||
|
||||
Ok(EGLSurface {
|
||||
display,
|
||||
native,
|
||||
display: display.display.clone(),
|
||||
native: Box::new(native),
|
||||
surface: AtomicPtr::new(surface as *mut _),
|
||||
config_id: config,
|
||||
pixel_format,
|
||||
|
@ -94,7 +82,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
}
|
||||
|
||||
/// 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 result = if !surface.is_null() {
|
||||
|
@ -115,14 +103,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
unsafe {
|
||||
self.native
|
||||
.create(&self.display, self.config_id, &self.surface_attributes)
|
||||
.map_err(|err| match err {
|
||||
SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
|
||||
SwapBuffersError::EGLCreateWindowSurface(err)
|
||||
}
|
||||
SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
|
||||
SwapBuffersError::Underlying(err)
|
||||
}
|
||||
})? as *mut _
|
||||
.map_err(SwapBuffersError::EGLCreateSurface)? as *mut _
|
||||
},
|
||||
Ordering::SeqCst,
|
||||
);
|
||||
|
@ -151,17 +132,30 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
unsafe {
|
||||
ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
|
||||
|
|
Loading…
Reference in New Issue