WIP: Rework egl and glium errors

This commit is contained in:
Victor Brekenfeld 2020-04-30 00:24:35 +02:00
parent 6c4a3817d3
commit 31b6d84442
17 changed files with 537 additions and 271 deletions

View File

@ -7,7 +7,7 @@ use glium::{
self, self,
index::PrimitiveType, index::PrimitiveType,
texture::{MipmapsOption, Texture2d, UncompressedFloatFormat}, texture::{MipmapsOption, Texture2d, UncompressedFloatFormat},
Frame, GlObject, Surface, GlObject, Surface,
}; };
use slog::Logger; use slog::Logger;
@ -16,7 +16,7 @@ use smithay::backend::egl::display::EGLBufferReader;
use smithay::{ use smithay::{
backend::{ backend::{
egl::{BufferAccessError, EGLImages, Format}, egl::{BufferAccessError, EGLImages, Format},
graphics::{gl::GLGraphicsBackend, glium::GliumGraphicsBackend}, graphics::{gl::GLGraphicsBackend, glium::{GliumGraphicsBackend, Frame}},
}, },
reexports::wayland_server::protocol::{wl_buffer, wl_surface}, reexports::wayland_server::protocol::{wl_buffer, wl_surface},
wayland::{ wayland::{
@ -159,7 +159,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() { let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
display.egl_buffer_contents(buffer) display.egl_buffer_contents(buffer)
} else { } else {
Err(BufferAccessError::NotManaged(buffer)) Err(BufferAccessError::NotManaged(buffer, smithay::backend::egl::EGLError::BadDisplay))
}; };
match images { match images {
Ok(images) => { Ok(images) => {
@ -193,7 +193,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
images: Some(images), // I guess we need to keep this alive ? images: Some(images), // I guess we need to keep this alive ?
}) })
} }
Err(BufferAccessError::NotManaged(buffer)) => { Err(BufferAccessError::NotManaged(buffer, _)) => {
// this is not an EGL buffer, try SHM // this is not an EGL buffer, try SHM
self.texture_from_shm_buffer(buffer) self.texture_from_shm_buffer(buffer)
} }
@ -235,7 +235,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn render_texture( pub fn render_texture(
&self, &self,
target: &mut glium::Frame, target: &mut Frame,
texture: &Texture2d, texture: &Texture2d,
texture_kind: usize, texture_kind: usize,
y_inverted: bool, y_inverted: bool,

View File

@ -9,7 +9,7 @@ use smithay::{
common::Error, common::Error,
device_bind, device_bind,
legacy::{LegacyDrmDevice, LegacyDrmSurface}, legacy::{LegacyDrmDevice, LegacyDrmSurface},
Device, DeviceHandler, RawSurface, Surface, Device, DeviceHandler, RawSurface,
}, },
reexports::{ reexports::{
calloop::EventLoop, calloop::EventLoop,

View File

@ -4,8 +4,8 @@
//! //!
use drm::control::{connector, crtc, Mode, RawResourceHandle}; use drm::control::{connector, crtc, Mode, RawResourceHandle};
use std::path::PathBuf; use std::path::PathBuf;
use crate::backend::graphics::SwapBuffersError;
pub mod fallback; pub mod fallback;
@ -71,3 +71,21 @@ pub enum Error {
#[error("Atomic Test failed for new properties on crtc ({0:?})")] #[error("Atomic Test failed for new properties on crtc ({0:?})")]
TestFailed(crtc::Handle), TestFailed(crtc::Handle),
} }
impl Into<SwapBuffersError> for Error {
fn into(self) -> SwapBuffersError {
match self {
x @ Error::DeviceInactive => SwapBuffersError::TemporaryFailure(Box::new(x)),
Error::Access {
errmsg: _,
dev: _,
source,
} if match source.get_ref() {
drm::SystemError::Unknown { errno: nix::errno::Errno::EBUSY } => true,
drm::SystemError::Unknown { errno: nix::errno::Errno::EINTR } => true,
_ => false,
} => SwapBuffersError::TemporaryFailure(Box::new(source)),
x => SwapBuffersError::ContextLost(Box::new(x)),
}
}
}

View File

@ -17,7 +17,7 @@ use wayland_server::Display;
use super::{Device, DeviceHandler, Surface}; use super::{Device, DeviceHandler, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::Error as EGLError; use crate::backend::egl::{Error as EGLError, EGLError as RawEGLError, SurfaceCreationError};
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
@ -33,8 +33,11 @@ pub mod session;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> { pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> {
/// EGL Error /// EGL Error
#[error("EGL error: {0:?}")] #[error("EGL error: {0:}")]
EGL(#[source] EGLError), EGL(#[source] EGLError),
/// EGL Error
#[error("EGL error: {0:}")]
RawEGL(#[source] RawEGLError),
/// Underlying backend error /// Underlying backend error
#[error("Underlying backend error: {0:?}")] #[error("Underlying backend error: {0:?}")]
Underlying(#[source] U), Underlying(#[source] U),
@ -46,7 +49,7 @@ type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
pub struct EglDevice<B, D> pub struct EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
dev: EGLDisplay<B, D>, dev: EGLDisplay<B, D>,
@ -58,7 +61,7 @@ where
impl<B, D> AsRawFd for EglDevice<B, D> impl<B, D> AsRawFd for EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
@ -69,7 +72,7 @@ where
impl<B, D> EglDevice<B, D> impl<B, D> EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
/// Try to create a new [`EglDevice`] from an open device. /// Try to create a new [`EglDevice`] from an open device.
@ -124,7 +127,7 @@ where
struct InternalDeviceHandler<B, D> struct InternalDeviceHandler<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
handler: Box<dyn DeviceHandler<Device = EglDevice<B, D>> + 'static>, handler: Box<dyn DeviceHandler<Device = EglDevice<B, D>> + 'static>,
@ -133,7 +136,7 @@ where
impl<B, D> DeviceHandler for InternalDeviceHandler<B, D> impl<B, D> DeviceHandler for InternalDeviceHandler<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
type Device = D; type Device = D;
@ -149,7 +152,7 @@ where
impl<B, D> Device for EglDevice<B, D> impl<B, D> Device for EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
type Surface = EglSurface<<D as Device>::Surface>; type Surface = EglSurface<<D as Device>::Surface>;
@ -188,7 +191,10 @@ where
context.get_config_id(), context.get_config_id(),
(crtc, mode, Vec::from(connectors)), (crtc, mode, Vec::from(connectors)),
) )
.map_err(Error::EGL)?; .map_err(|err| match err {
SurfaceCreationError::EGLSurfaceCreationFailed(err) => Error::RawEGL(err),
SurfaceCreationError::NativeSurfaceCreationFailed(err) => Error::Underlying(err),
})?;
Ok(EglSurface { context, surface }) Ok(EglSurface { context, surface })
} }
@ -225,7 +231,7 @@ where
impl<B, D> EGLGraphicsBackend for EglDevice<B, D> impl<B, D> EGLGraphicsBackend for EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, EGLError> { fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, EGLError> {
@ -236,7 +242,7 @@ where
impl<B, D> Drop for EglDevice<B, D> impl<B, D> Drop for EglDevice<B, D>
where where
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static, D: Device + NativeDisplay<B, Arguments = Arguments, Error=<<D as Device>::Surface as Surface>::Error> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
fn drop(&mut self) { fn drop(&mut self) {

View File

@ -7,7 +7,7 @@ use drm::control::{crtc, connector, Mode};
use std::os::unix::io::RawFd; use std::os::unix::io::RawFd;
use super::EglDevice; use super::EglDevice;
use crate::backend::drm::Device; use crate::backend::drm::{Device, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::session::{AsSessionObserver, SessionObserver}; use crate::backend::session::{AsSessionObserver, SessionObserver};
@ -22,7 +22,7 @@ impl<S, B, D> AsSessionObserver<EglDeviceObserver<S>> for EglDevice<B, D>
where where
S: SessionObserver + 'static, S: SessionObserver + 'static,
B: Backend<Surface = <D as Device>::Surface> + 'static, B: Backend<Surface = <D as Device>::Surface> + 'static,
D: Device + NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>)> + AsSessionObserver<S> + 'static, D: Device + NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>), Error=<<D as Device>::Surface as Surface>::Error> + AsSessionObserver<S> + 'static,
<D as Device>::Surface: NativeSurface, <D as Device>::Surface: NativeSurface,
{ {
fn observer(&mut self) -> EglDeviceObserver<S> { fn observer(&mut self) -> EglDeviceObserver<S> {

View File

@ -1,5 +1,6 @@
use drm::control::{connector, crtc, Mode}; use drm::control::{connector, crtc, Mode};
use nix::libc::c_void; use nix::libc::c_void;
use std::convert::TryInto;
use super::Error; use super::Error;
use crate::backend::drm::Surface; use crate::backend::drm::Surface;
@ -22,7 +23,7 @@ where
impl<N> Surface for EglSurface<N> impl<N> Surface for EglSurface<N>
where where
N: NativeSurface + Surface, N: native::NativeSurface + Surface,
{ {
type Connectors = <N as Surface>::Connectors; type Connectors = <N as Surface>::Connectors;
type Error = Error<<N as Surface>::Error>; type Error = Error<<N as Surface>::Error>;
@ -90,9 +91,15 @@ where
impl<N> GLGraphicsBackend for EglSurface<N> impl<N> GLGraphicsBackend for EglSurface<N>
where where
N: native::NativeSurface + Surface, N: native::NativeSurface + Surface,
<N as NativeSurface>::Error: Into<SwapBuffersError> + 'static,
{ {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.surface.swap_buffers() if let Err(err) = self.surface.swap_buffers() {
Err(match err.try_into() {
Ok(x) => x,
Err(x) => x.into(),
})
} else { Ok(()) }
} }
fn get_proc_address(&self, symbol: &str) -> *const c_void { fn get_proc_address(&self, symbol: &str) -> *const c_void {
@ -109,7 +116,7 @@ where
} }
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.context.make_current_with_surface(&self.surface) self.context.make_current_with_surface(&self.surface).map_err(Into::into)
} }
fn get_pixel_format(&self) -> PixelFormat { fn get_pixel_format(&self) -> PixelFormat {

View File

@ -7,8 +7,7 @@
use crate::backend::drm::{Device, RawDevice, Surface}; use crate::backend::drm::{Device, RawDevice, Surface};
use crate::backend::egl::ffi; use crate::backend::egl::ffi;
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::Error as EglError; use crate::backend::egl::{Error as EglBackendError, EGLError, wrap_egl_call};
use crate::backend::graphics::SwapBuffersError;
use super::{Error, GbmDevice, GbmSurface}; use super::{Error, GbmDevice, GbmSurface};
@ -31,22 +30,22 @@ impl<D: RawDevice + 'static> Backend for Gbm<D> {
display: ffi::NativeDisplayType, display: ffi::NativeDisplayType,
has_dp_extension: F, has_dp_extension: F,
log: ::slog::Logger, log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,
{ {
if has_dp_extension("EGL_KHR_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() { if has_dp_extension("EGL_KHR_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm"); trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null()))
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplayEXT::is_loaded() { } else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm"); trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null()))
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() { } else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm"); trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null()))
} else { } else {
trace!(log, "Default EGL Display Initialization via GetDisplay"); trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _) wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
} }
} }
} }
@ -59,7 +58,7 @@ unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for Gb
true true
} }
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglError> { fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> {
Ok(self.dev.borrow().as_raw() as *const _) Ok(self.dev.borrow().as_raw() as *const _)
} }
@ -69,6 +68,8 @@ unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for Gb
} }
unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> { unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
type Error = Error<<<D as RawDevice>::Surface as Surface>::Error>;
fn ptr(&self) -> ffi::NativeWindowType { fn ptr(&self) -> ffi::NativeWindowType {
self.0.surface.borrow().as_raw() as *const _ self.0.surface.borrow().as_raw() as *const _
} }
@ -77,25 +78,13 @@ unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
self.needs_recreation() self.needs_recreation()
} }
fn recreate(&self) -> bool { fn recreate(&self) -> Result<(), Self::Error> {
if let Err(err) = GbmSurface::recreate(self) { GbmSurface::recreate(self)
error!(self.0.logger, "Failure recreating internal resources: {}", err);
false
} else {
true
}
} }
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> Result<(), Self::Error> {
// this is safe since `eglSwapBuffers` will have been called exactly once // this is safe since `eglSwapBuffers` will have been called exactly once
// if this is used by our egl module, which is why this trait is unsafe. // if this is used by our egl module, which is why this trait is unsafe.
match unsafe { self.page_flip() } { unsafe { self.page_flip() }
Ok(()) => Ok(()),
Err(Error::FrontBuffersExhausted) => Err(SwapBuffersError::AlreadySwapped),
Err(err) => {
warn!(self.0.logger, "Page flip failed: {}", err);
Err(SwapBuffersError::Unknown(0))
}
}
} }
} }

View File

@ -10,6 +10,7 @@
//! //!
use super::{Device, DeviceHandler, RawDevice, ResourceHandles, Surface}; use super::{Device, DeviceHandler, RawDevice, ResourceHandles, Surface};
use crate::backend::graphics::SwapBuffersError;
use drm::control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode}; use drm::control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode};
use drm::SystemError as DrmError; use drm::SystemError as DrmError;
@ -26,7 +27,7 @@ use std::sync::Once;
/// Errors thrown by the [`GbmDevice`](::backend::drm::gbm::GbmDevice) /// Errors thrown by the [`GbmDevice`](::backend::drm::gbm::GbmDevice)
/// and [`GbmSurface`](::backend::drm::gbm::GbmSurface). /// and [`GbmSurface`](::backend::drm::gbm::GbmSurface).
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> { pub enum Error<U: std::error::Error + 'static> {
/// Creation of GBM device failed /// Creation of GBM device failed
#[error("Creation of GBM device failed")] #[error("Creation of GBM device failed")]
InitFailed(#[source] io::Error), InitFailed(#[source] io::Error),
@ -256,3 +257,21 @@ impl<D: RawDevice + ControlDevice + 'static> Drop for GbmDevice<D> {
self.clear_handler(); self.clear_handler();
} }
} }
impl<E> Into<SwapBuffersError> for Error<E>
where
E: std::error::Error + Into<SwapBuffersError> + 'static
{
fn into(self) -> SwapBuffersError {
match self {
Error::FrontBuffersExhausted => SwapBuffersError::AlreadySwapped,
Error::FramebufferCreationFailed(x) if match x.get_ref() {
&drm::SystemError::Unknown { errno: nix::errno::Errno::EBUSY } => true,
&drm::SystemError::Unknown { errno: nix::errno::Errno::EINTR } => true,
_ => false
} => SwapBuffersError::TemporaryFailure(Box::new(x)),
Error::Underlying(x) => x.into(),
x => SwapBuffersError::ContextLost(Box::new(x)),
}
}
}

View File

@ -1,10 +1,10 @@
//! EGL context related structs //! EGL context related structs
use super::{ffi, Error}; use super::{ffi, Error, MakeCurrentError, wrap_egl_call};
use crate::backend::egl::display::{EGLDisplay, EGLDisplayHandle}; use crate::backend::egl::display::{EGLDisplay, EGLDisplayHandle};
use crate::backend::egl::native::NativeSurface; use crate::backend::egl::native::NativeSurface;
use crate::backend::egl::{native, EGLSurface}; use crate::backend::egl::{native, EGLSurface};
use crate::backend::graphics::{PixelFormat, SwapBuffersError}; 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::Arc; use std::sync::Arc;
@ -93,21 +93,14 @@ impl EGLContext {
trace!(log, "Creating EGL context..."); trace!(log, "Creating EGL context...");
// TODO: Support shared contexts // TODO: Support shared contexts
let context = unsafe { let context = wrap_egl_call(|| unsafe {
ffi::egl::CreateContext( ffi::egl::CreateContext(
**display.display, **display.display,
config_id, config_id,
ptr::null(), ptr::null(),
context_attributes.as_ptr(), context_attributes.as_ptr(),
) )
}; }).map_err(Error::CreationFailed)?;
if context.is_null() {
match unsafe { ffi::egl::GetError() } as u32 {
ffi::egl::BAD_ATTRIBUTE => return Err(Error::CreationFailed),
err_no => return Err(Error::Unknown(err_no)),
}
}
info!(log, "EGL context created"); info!(log, "EGL context created");
@ -129,22 +122,13 @@ impl EGLContext {
pub unsafe fn make_current_with_surface<N>( pub unsafe fn make_current_with_surface<N>(
&self, &self,
surface: &EGLSurface<N>, surface: &EGLSurface<N>,
) -> ::std::result::Result<(), SwapBuffersError> ) -> Result<(), MakeCurrentError>
where where
N: NativeSurface, N: NativeSurface,
{ {
let surface_ptr = surface.surface.get(); let surface_ptr = surface.surface.get();
let ret = ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context); wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context)).map(|_| ()).map_err(Into::into)
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} }
/// Makes the OpenGL context the current context in the current thread with no surface bound. /// Makes the OpenGL context the current context in the current thread with no surface bound.
@ -152,18 +136,9 @@ impl EGLContext {
/// # Safety /// # Safety
/// ///
/// 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 without being unbound again (see `unbind`)
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
let ret = ffi::egl::MakeCurrent(**self.display, ptr::null(), ptr::null(), self.context); wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, self.context)).map(|_| ()).map_err(Into::into)
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} }
/// Returns true if the OpenGL context is the current one in the thread. /// Returns true if the OpenGL context is the current one in the thread.
@ -180,16 +155,24 @@ impl EGLContext {
pub fn get_pixel_format(&self) -> PixelFormat { pub fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format self.pixel_format
} }
/// Unbinds this context from the current thread, if set.
///
/// This does nothing if this context is not the current context
pub fn unbind(&self) -> Result<(), MakeCurrentError> {
if self.is_current() {
wrap_egl_call(|| unsafe { ffi::egl::MakeCurrent(**self.display, ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, ffi::egl::NO_CONTEXT)})?;
}
Ok(())
}
} }
impl Drop for EGLContext { impl Drop for EGLContext {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// 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
if ffi::egl::GetCurrentContext() == self.context as *const _ { // ignore failures at this point
ffi::egl::MakeCurrent(ptr::null(), ptr::null(), ptr::null(), ptr::null()); let _ = self.unbind();
}
ffi::egl::DestroyContext(**self.display, self.context); ffi::egl::DestroyContext(**self.display, self.context);
} }
} }
@ -270,7 +253,7 @@ impl Default for PixelFormatRequirements {
impl PixelFormatRequirements { impl PixelFormatRequirements {
/// Append the requirements to the given attribute list /// Append the requirements to the given attribute list
pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) -> Result<(), Error> { pub fn create_attributes(&self, out: &mut Vec<c_int>, logger: &slog::Logger) -> Result<(), ()> {
if let Some(hardware_accelerated) = self.hardware_accelerated { if let Some(hardware_accelerated) = self.hardware_accelerated {
out.push(ffi::egl::CONFIG_CAVEAT as c_int); out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated { out.push(if hardware_accelerated {
@ -328,7 +311,7 @@ impl PixelFormatRequirements {
if self.stereoscopy { if self.stereoscopy {
error!(logger, "Stereoscopy is currently unsupported (sorry!)"); error!(logger, "Stereoscopy is currently unsupported (sorry!)");
return Err(Error::NoAvailablePixelFormat); return Err(());
} }
Ok(()) Ok(())

View File

@ -3,7 +3,7 @@
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use crate::backend::egl::EGLGraphicsBackend; use crate::backend::egl::EGLGraphicsBackend;
use crate::backend::egl::{ use crate::backend::egl::{
ffi, get_proc_address, native, BufferAccessError, EGLContext, EGLImages, EGLSurface, Error, Format, ffi, get_proc_address, native, BufferAccessError, SurfaceCreationError, EGLContext, EGLImages, EGLSurface, Error, Format, EGLError, wrap_egl_call,
}; };
use std::sync::Arc; use std::sync::Arc;
@ -44,6 +44,7 @@ impl Deref for EGLDisplayHandle {
impl Drop for EGLDisplayHandle { impl Drop for EGLDisplayHandle {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
// ignore errors on drop
ffi::egl::Terminate(self.handle); ffi::egl::Terminate(self.handle);
} }
} }
@ -93,7 +94,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
// 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 = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); let p = wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)).map_err(Error::InitFailed)?;
// 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
@ -105,23 +106,16 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
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, "EGL No-Display Extensions: {:?}", dp_extensions);
let display = let display = unsafe { B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()).map_err(Error::DisplayNotSupported)? };
unsafe { B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()) };
if display == ffi::egl::NO_DISPLAY {
error!(log, "EGL Display is not valid");
return Err(Error::DisplayNotSupported);
}
let egl_version = { let egl_version = {
let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit(); let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit(); let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
if unsafe { ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) } == 0 { wrap_egl_call(|| unsafe { ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) }).map_err(Error::InitFailed)?;
return Err(Error::InitFailed);
}
let major = unsafe { major.assume_init() }; let major = unsafe { major.assume_init() };
let minor = unsafe { minor.assume_init() }; let minor = unsafe { minor.assume_init() };
@ -134,19 +128,18 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
// the list of extensions supported by the client once initialized is different from the // the list of extensions supported by the client once initialized is different from the
// list of extensions obtained earlier // list of extensions obtained earlier
let extensions = if egl_version >= (1, 2) { let extensions = if egl_version >= (1, 2) {
let p = unsafe { CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32)) }; let p = unsafe { CStr::from_ptr(wrap_egl_call(|| ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32)).map_err(Error::InitFailed)?) };
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<_>>()
} else { } else {
vec![] vec![]
}; };
info!(log, "EGL Extensions: {:?}", extensions); info!(log, "EGL Extensions: {:?}", extensions);
if egl_version >= (1, 2) && unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) } == 0 { if egl_version >= (1, 2) {
error!(log, "OpenGLES not supported by the underlying EGL implementation"); return Err(Error::OpenGlesNotSupported(None));
return Err(Error::OpenGlesNotSupported);
} }
wrap_egl_call(|| unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) }).map_err(|source| Error::OpenGlesNotSupported(Some(source)))?;
Ok(EGLDisplay { Ok(EGLDisplay {
native: RefCell::new(native), native: RefCell::new(native),
@ -219,15 +212,15 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
} }
}; };
reqs.create_attributes(&mut out, &self.logger)?; reqs.create_attributes(&mut out, &self.logger).map_err(|()| Error::NoAvailablePixelFormat)?;
out.push(ffi::egl::NONE as c_int); out.push(ffi::egl::NONE as c_int);
out out
}; };
// calling `eglChooseConfig` // calling `eglChooseConfig`
let mut num_configs = unsafe { std::mem::zeroed() }; let mut num_configs = 0;
if unsafe { wrap_egl_call(|| unsafe {
ffi::egl::ChooseConfig( ffi::egl::ChooseConfig(
**self.display, **self.display,
descriptor.as_ptr(), descriptor.as_ptr(),
@ -235,18 +228,13 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
0, 0,
&mut num_configs, &mut num_configs,
) )
} == 0 }).map_err(Error::ConfigFailed)?;
{
return Err(Error::ConfigFailed);
}
if num_configs == 0 { if num_configs == 0 {
return Err(Error::NoAvailablePixelFormat); return Err(Error::NoAvailablePixelFormat);
} }
let mut config_ids = Vec::with_capacity(num_configs as usize); let mut config_ids: Vec<ffi::egl::types::EGLConfig> = Vec::with_capacity(num_configs as usize);
config_ids.resize_with(num_configs as usize, || unsafe { std::mem::zeroed() }); wrap_egl_call(|| unsafe {
if unsafe {
ffi::egl::ChooseConfig( ffi::egl::ChooseConfig(
**self.display, **self.display,
descriptor.as_ptr(), descriptor.as_ptr(),
@ -254,44 +242,43 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
num_configs, num_configs,
&mut num_configs, &mut num_configs,
) )
} == 0 }).map_err(Error::ConfigFailed)?;
{ unsafe { config_ids.set_len(num_configs as usize); }
return Err(Error::ConfigFailed);
}
// TODO: Deeper swap intervals might have some uses // TODO: Deeper swap intervals might have some uses
let desired_swap_interval = if attributes.vsync { 1 } else { 0 }; let desired_swap_interval = if attributes.vsync { 1 } else { 0 };
let config_ids = config_ids let config_ids = config_ids
.into_iter() .into_iter()
.filter(|&config| unsafe { .map(|config| unsafe {
let mut min_swap_interval = 0; let mut min_swap_interval = 0;
ffi::egl::GetConfigAttrib( wrap_egl_call(|| ffi::egl::GetConfigAttrib(
**self.display, **self.display,
config, config,
ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint, ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,
&mut min_swap_interval, &mut min_swap_interval,
); ))?;
if desired_swap_interval < min_swap_interval { if desired_swap_interval < min_swap_interval {
return false; return Ok(None);
} }
let mut max_swap_interval = 0; let mut max_swap_interval = 0;
ffi::egl::GetConfigAttrib( wrap_egl_call(|| ffi::egl::GetConfigAttrib(
**self.display, **self.display,
config, config,
ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint, ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,
&mut max_swap_interval, &mut max_swap_interval,
); ))?;
if desired_swap_interval > max_swap_interval { if desired_swap_interval > max_swap_interval {
return false; return Ok(None);
} }
true Ok(Some(config))
}) })
.collect::<Vec<_>>(); .collect::<Result<Vec<Option<ffi::egl::types::EGLConfig>>, EGLError>>().map_err(Error::ConfigFailed)?
.into_iter().flat_map(|x| x).collect::<Vec<_>>();
if config_ids.is_empty() { if config_ids.is_empty() {
return Err(Error::NoAvailablePixelFormat); return Err(Error::NoAvailablePixelFormat);
@ -304,15 +291,12 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
macro_rules! attrib { macro_rules! attrib {
($display:expr, $config:expr, $attr:expr) => {{ ($display:expr, $config:expr, $attr:expr) => {{
let mut value = MaybeUninit::uninit(); let mut value = MaybeUninit::uninit();
let res = ffi::egl::GetConfigAttrib( wrap_egl_call(|| ffi::egl::GetConfigAttrib(
**$display, **$display,
$config, $config,
$attr as ffi::egl::types::EGLint, $attr as ffi::egl::types::EGLint,
value.as_mut_ptr(), value.as_mut_ptr(),
); )).map_err(Error::ConfigFailed)?;
if res == 0 {
return Err(Error::ConfigFailed);
}
value.assume_init() value.assume_init()
}}; }};
}; };
@ -357,12 +341,9 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
double_buffer: Option<bool>, double_buffer: Option<bool>,
config: ffi::egl::types::EGLConfig, config: ffi::egl::types::EGLConfig,
args: N::Arguments, args: N::Arguments,
) -> Result<EGLSurface<B::Surface>, Error> { ) -> Result<EGLSurface<B::Surface>, SurfaceCreationError<N::Error>> {
trace!(self.logger, "Creating EGL window surface."); trace!(self.logger, "Creating EGL window surface.");
let surface = self.native.borrow_mut().create_surface(args).map_err(|e| { let surface = self.native.borrow_mut().create_surface(args).map_err(SurfaceCreationError::NativeSurfaceCreationFailed)?;
error!(self.logger, "EGL surface creation failed: {}", e);
Error::SurfaceCreationFailed
})?;
EGLSurface::new( EGLSurface::new(
self.display.clone(), self.display.clone(),
@ -375,7 +356,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
.map(|x| { .map(|x| {
debug!(self.logger, "EGL surface successfully created"); debug!(self.logger, "EGL surface successfully created");
x x
}) }).map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
} }
/// Returns the runtime egl version of this display /// Returns the runtime egl version of this display
@ -426,10 +407,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGL
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"]));
} }
let res = unsafe { ffi::egl::BindWaylandDisplayWL(**self.display, display.c_ptr() as *mut _) }; wrap_egl_call(|| unsafe { ffi::egl::BindWaylandDisplayWL(**self.display, display.c_ptr() as *mut _) }).map_err(Error::OtherEGLDisplayAlreadyBound)?;
if res == 0 {
return Err(Error::OtherEGLDisplayAlreadyBound);
}
Ok(EGLBufferReader::new(self.display.clone(), display.c_ptr())) Ok(EGLBufferReader::new(self.display.clone(), display.c_ptr()))
} }
} }
@ -469,16 +447,15 @@ impl EGLBufferReader {
buffer: WlBuffer, buffer: WlBuffer,
) -> ::std::result::Result<EGLImages, BufferAccessError> { ) -> ::std::result::Result<EGLImages, BufferAccessError> {
let mut format: i32 = 0; let mut format: i32 = 0;
if unsafe { wrap_egl_call(|| unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
**self.display, **self.display,
buffer.as_ref().c_ptr() as _, buffer.as_ref().c_ptr() as _,
ffi::egl::EGL_TEXTURE_FORMAT, ffi::egl::EGL_TEXTURE_FORMAT,
&mut format, &mut format,
) == 0 )
} { }).map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
return Err(BufferAccessError::NotManaged(buffer));
}
let format = match format { let format = match format {
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA, x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
@ -490,40 +467,34 @@ impl EGLBufferReader {
}; };
let mut width: i32 = 0; let mut width: i32 = 0;
if unsafe { wrap_egl_call(|| unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
**self.display, **self.display,
buffer.as_ref().c_ptr() as _, buffer.as_ref().c_ptr() as _,
ffi::egl::WIDTH as i32, ffi::egl::WIDTH as i32,
&mut width, &mut width,
) == 0 )
} { }).map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
return Err(BufferAccessError::NotManaged(buffer));
}
let mut height: i32 = 0; let mut height: i32 = 0;
if unsafe { wrap_egl_call(|| unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
**self.display, **self.display,
buffer.as_ref().c_ptr() as _, buffer.as_ref().c_ptr() as _,
ffi::egl::HEIGHT as i32, ffi::egl::HEIGHT as i32,
&mut height, &mut height,
) == 0 )
} { }).map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
return Err(BufferAccessError::NotManaged(buffer));
}
let mut inverted: i32 = 0; let mut inverted: i32 = 0;
if unsafe { wrap_egl_call(|| unsafe {
ffi::egl::QueryWaylandBufferWL( ffi::egl::QueryWaylandBufferWL(
**self.display, **self.display,
buffer.as_ref().c_ptr() as _, buffer.as_ref().c_ptr() as _,
ffi::egl::WAYLAND_Y_INVERTED_WL, ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted, &mut inverted,
) != 0 )
} { }).map_err(|source| BufferAccessError::NotManaged(buffer.clone(), source))?;
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes()); let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() { for i in 0..format.num_planes() {
@ -533,7 +504,7 @@ impl EGLBufferReader {
out.push(ffi::egl::NONE as i32); out.push(ffi::egl::NONE as i32);
images.push({ images.push({
let image = unsafe { let image = wrap_egl_call(|| unsafe {
ffi::egl::CreateImageKHR( ffi::egl::CreateImageKHR(
**self.display, **self.display,
ffi::egl::NO_CONTEXT, ffi::egl::NO_CONTEXT,
@ -541,12 +512,8 @@ impl EGLBufferReader {
buffer.as_ref().c_ptr() as *mut _, buffer.as_ref().c_ptr() as *mut _,
out.as_ptr(), out.as_ptr(),
) )
}; }).map_err(BufferAccessError::EGLImageCreationFailed)?;
if image == ffi::egl::NO_IMAGE_KHR { image
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
}); });
} }
@ -601,6 +568,7 @@ impl Drop for EGLBufferReader {
fn drop(&mut self) { fn drop(&mut self) {
if !self.wayland.is_null() { if !self.wayland.is_null() {
unsafe { unsafe {
// ignore errors on drop
ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _); ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);
} }
} }

View File

@ -1,3 +1,5 @@
use super::ffi;
#[derive(thiserror::Error, Debug)] #[derive(thiserror::Error, Debug)]
/// EGL errors /// EGL errors
pub enum Error { pub enum Error {
@ -5,8 +7,8 @@ pub enum Error {
#[error("The requested OpenGL version {0:?} is not supported")] #[error("The requested OpenGL version {0:?} is not supported")]
OpenGlVersionNotSupported((u8, u8)), OpenGlVersionNotSupported((u8, u8)),
/// The EGL implementation does not support creating OpenGL ES contexts /// The EGL implementation does not support creating OpenGL ES contexts
#[error("The EGL implementation does not support creating OpenGL ES contexts")] #[error("The EGL implementation does not support creating OpenGL ES contexts. Err: {0:?}")]
OpenGlesNotSupported, OpenGlesNotSupported(#[source] Option<EGLError>),
/// No available pixel format matched the criteria /// No available pixel format matched the criteria
#[error("No available pixel format matched the criteria")] #[error("No available pixel format matched the criteria")]
NoAvailablePixelFormat, NoAvailablePixelFormat,
@ -14,26 +16,23 @@ pub enum Error {
#[error("The expected backend '{0:?}' does not match the runtime")] #[error("The expected backend '{0:?}' does not match the runtime")]
NonMatchingBackend(&'static str), NonMatchingBackend(&'static str),
/// Unable to obtain a valid EGL Display /// Unable to obtain a valid EGL Display
#[error("Unable to obtain a valid EGL Display")] #[error("Unable to obtain a valid EGL Display. Err: {0:}")]
DisplayNotSupported, DisplayNotSupported(#[source] EGLError),
/// `eglInitialize` returned an error /// `eglInitialize` returned an error
#[error("Failed to initialize EGL")] #[error("Failed to initialize EGL. Err: {0:}")]
InitFailed, InitFailed(#[source] EGLError),
/// Failed to configure the EGL context /// Failed to configure the EGL context
#[error("Failed to configure the EGL context")] #[error("Failed to configure the EGL context")]
ConfigFailed, ConfigFailed(#[source] EGLError),
/// Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements /// Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements
#[error("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements")] #[error("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements. Err: {0:}")]
CreationFailed, CreationFailed(#[source] EGLError),
/// `eglCreateWindowSurface` failed
#[error("`eglCreateWindowSurface` failed")]
SurfaceCreationFailed,
/// The required EGL extension is not supported by the underlying EGL implementation /// The required EGL extension is not supported by the underlying EGL implementation
#[error("None of the following EGL extensions is supported by the underlying EGL implementation, at least one is required: {0:?}")] #[error("None of the following EGL extensions is supported by the underlying EGL implementation, at least one is required: {0:?}")]
EglExtensionNotSupported(&'static [&'static str]), EglExtensionNotSupported(&'static [&'static str]),
/// Only one EGLDisplay may be bound to a given `WlDisplay` at any time /// Only one EGLDisplay may be bound to a given `WlDisplay` at any time
#[error("Only one EGLDisplay may be bound to a given `WlDisplay` at any time")] #[error("Only one EGLDisplay may be bound to a given `WlDisplay` at any time")]
OtherEGLDisplayAlreadyBound, OtherEGLDisplayAlreadyBound(#[source] EGLError),
/// No EGLDisplay is currently bound to this `WlDisplay` /// No EGLDisplay is currently bound to this `WlDisplay`
#[error("No EGLDisplay is currently bound to this `WlDisplay`")] #[error("No EGLDisplay is currently bound to this `WlDisplay`")]
NoEGLDisplayBound, NoEGLDisplayBound,
@ -43,7 +42,89 @@ pub enum Error {
/// Failed to create `EGLImages` from the buffer /// Failed to create `EGLImages` from the buffer
#[error("Failed to create `EGLImages` from the buffer")] #[error("Failed to create `EGLImages` from the buffer")]
EGLImageCreationFailed, EGLImageCreationFailed,
/// The reason of failure could not be determined }
#[error("Unknown error: {0}")]
/// Raw EGL error
#[derive(thiserror::Error, Debug)]
pub enum EGLError {
/// EGL is not initialized, or could not be initialized, for the specified EGL display connection.
#[error("EGL is not initialized, or could not be initialized, for the specified EGL display connection.")]
NotInitialized,
/// EGL cannot access a requested resource (for example a context is bound in another thread).
#[error("EGL cannot access a requested resource (for example a context is bound in another thread).")]
BadAccess,
/// EGL failed to allocate resources for the requested operation.
#[error("EGL failed to allocate resources for the requested operation.")]
BadAlloc,
/// An unrecognized attribute or attribute value was passed in the attribute list.
#[error("An unrecognized attribute or attribute value was passed in the attribute list.")]
BadAttribute,
/// An EGLContext argument does not name a valid EGL rendering context.
#[error("An EGLContext argument does not name a valid EGL rendering context.")]
BadContext,
/// An EGLConfig argument does not name a valid EGL frame buffer configuration.
#[error("An EGLConfig argument does not name a valid EGL frame buffer configuration.")]
BadConfig,
/// The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid.
#[error("The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid.")]
BadCurrentSurface,
/// An EGLDisplay argument does not name a valid EGL display connection.
#[error("An EGLDisplay argument does not name a valid EGL display connection.")]
BadDisplay,
/// An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering.
#[error("An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering.")]
BadSurface,
/// Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface).
#[error("Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface).")]
BadMatch,
/// One or more argument values are invalid.
#[error("One or more argument values are invalid.")]
BadParameter,
/// A NativePixmapType argument does not refer to a valid native pixmap.
#[error("A NativePixmapType argument does not refer to a valid native pixmap.")]
BadNativePixmap,
/// A NativeWindowType argument does not refer to a valid native window.
#[error("A NativeWindowType argument does not refer to a valid native window.")]
BadNativeWindow,
/// A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.
#[error("A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.")]
ContextLost,
/// An unknown error
#[error("An unknown error ({0:x})")]
Unknown(u32), Unknown(u32),
} }
impl From<u32> for EGLError {
fn from(value: u32) -> Self {
match value {
ffi::egl::NOT_INITIALIZED => EGLError::NotInitialized,
ffi::egl::BAD_ACCESS => EGLError::BadAccess,
ffi::egl::BAD_ALLOC => EGLError::BadAlloc,
ffi::egl::BAD_ATTRIBUTE => EGLError::BadAttribute,
ffi::egl::BAD_CONTEXT => EGLError::BadContext,
ffi::egl::BAD_CURRENT_SURFACE => EGLError::BadCurrentSurface,
ffi::egl::BAD_DISPLAY => EGLError::BadDisplay,
ffi::egl::BAD_SURFACE => EGLError::BadSurface,
ffi::egl::BAD_MATCH => EGLError::BadMatch,
ffi::egl::BAD_PARAMETER => EGLError::BadParameter,
ffi::egl::BAD_NATIVE_PIXMAP => EGLError::BadNativePixmap,
ffi::egl::BAD_NATIVE_WINDOW => EGLError::BadNativeWindow,
ffi::egl::CONTEXT_LOST => EGLError::ContextLost,
x => EGLError::Unknown(x),
}
}
}
impl EGLError {
fn from_last_call() -> Result<(), EGLError> {
match unsafe { ffi::egl::GetError() as u32 } {
ffi::egl::SUCCESS => Ok(()),
x => Err(EGLError::from(x)),
}
}
}
pub(crate) fn wrap_egl_call<R, F: FnOnce() -> R>(call: F) -> Result<R, EGLError> {
let res = call();
EGLError::from_last_call().map(|()| res)
}

View File

@ -19,7 +19,7 @@
//! 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::gl::{ffi as gl_ffi, GLGraphicsBackend}; use crate::backend::graphics::{SwapBuffersError as GraphicsSwapBuffersError, gl::{ffi as gl_ffi, GLGraphicsBackend}};
use nix::libc::c_uint; use nix::libc::c_uint;
use std::fmt; use std::fmt;
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
@ -28,7 +28,7 @@ use wayland_server::{protocol::wl_buffer::WlBuffer, 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::Error; pub use self::error::*;
use nix::libc::c_void; use nix::libc::c_void;
@ -84,11 +84,11 @@ pub enum BufferAccessError {
#[error("The corresponding context was lost")] #[error("The corresponding context was lost")]
ContextLost, ContextLost,
/// This buffer is not managed by the EGL buffer /// This buffer is not managed by the EGL buffer
#[error("This buffer is not managed by EGL")] #[error("This buffer is not managed by EGL. Err: {1:}")]
NotManaged(WlBuffer), NotManaged(WlBuffer, #[source] EGLError),
/// Failed to create `EGLImages` from the buffer /// Failed to create `EGLImages` from the buffer
#[error("Failed to create EGLImages from the buffer")] #[error("Failed to create EGLImages from the buffer. Err: {0:}")]
EGLImageCreationFailed, EGLImageCreationFailed(#[source] EGLError),
/// The required EGL extension is not supported by the underlying EGL implementation /// The required EGL extension is not supported by the underlying EGL implementation
#[error("{0}")] #[error("{0}")]
EglExtensionNotSupported(#[from] EglExtensionNotSupportedError), EglExtensionNotSupported(#[from] EglExtensionNotSupportedError),
@ -99,8 +99,8 @@ impl fmt::Debug for BufferAccessError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
match *self { match *self {
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"), BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"), BufferAccessError::NotManaged(_, _) => write!(formatter, "BufferAccessError::NotManaged"),
BufferAccessError::EGLImageCreationFailed => { BufferAccessError::EGLImageCreationFailed(_) => {
write!(formatter, "BufferAccessError::EGLImageCreationFailed") write!(formatter, "BufferAccessError::EGLImageCreationFailed")
} }
BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err), BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
@ -108,6 +108,78 @@ 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),
/// EGL error during `eglSwapBuffers`
#[error("{0:}")]
EGLSwapBuffers(#[source] EGLError),
/// EGL error during `eglCreateWindowSurface`
#[error("{0:}")]
EGLCreateWindowSurface(#[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> {
match value {
// bad surface is answered with a surface recreation in `swap_buffers`
x @ SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface) => Ok(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))),
SwapBuffersError::Underlying(e) => Err(e),
}
}
}
/// Error that can happen when making a context (and surface) current on the active thread.
#[derive(thiserror::Error, Debug)]
#[error("`eglMakeCurrent` failed: {0}")]
pub struct MakeCurrentError(#[from] EGLError);
impl From<MakeCurrentError> for GraphicsSwapBuffersError {
fn from(err: MakeCurrentError) -> GraphicsSwapBuffersError {
match err {
/*
From khronos docs:
If draw or read are not compatible with context, then an EGL_BAD_MATCH error is generated.
If context is current to some other thread, or if either draw or read are bound to contexts in another thread, an EGL_BAD_ACCESS error is generated.
If binding context would exceed the number of current contexts of that client API type supported by the implementation, an EGL_BAD_ACCESS error is generated.
If either draw or read are pbuffers created with eglCreatePbufferFromClientBuffer, and the underlying bound client API buffers are in use by the client API that created them, an EGL_BAD_ACCESS error is generated.
Except for the first case all of these recoverable. This conversation is mostly used in winit & EglSurface, where compatible context and surfaces are build.
*/
x @ MakeCurrentError(EGLError::BadAccess) => GraphicsSwapBuffersError::TemporaryFailure(Box::new(x)),
// BadSurface would result in a recreation in `eglSwapBuffers` -> recoverable
x @ MakeCurrentError(EGLError::BadSurface) => GraphicsSwapBuffersError::TemporaryFailure(Box::new(x)),
/*
From khronos docs:
If the previous context of the calling thread has unflushed commands, and the previous surface is no longer valid, an EGL_BAD_CURRENT_SURFACE error is generated.
This does not consern this or future `makeCurrent`-calls.
*/
x @ MakeCurrentError(EGLError::BadCurrentSurface) => GraphicsSwapBuffersError::TemporaryFailure(Box::new(x)),
// the rest is either never happening or are unrecoverable
x => GraphicsSwapBuffersError::ContextLost(Box::new(x)),
}
}
}
/// Error that might happen when binding an `EGLImage` to a GL texture /// Error that might happen when binding an `EGLImage` to a GL texture
#[derive(Debug, Clone, PartialEq, thiserror::Error)] #[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum TextureCreationError { pub enum TextureCreationError {
@ -249,6 +321,7 @@ impl EGLImages {
impl Drop for EGLImages { impl Drop for EGLImages {
fn drop(&mut self) { fn drop(&mut self) {
for image in self.images.drain(..) { for image in self.images.drain(..) {
// ignore result on drop
unsafe { unsafe {
ffi::egl::DestroyImageKHR(**self.display, image); ffi::egl::DestroyImageKHR(**self.display, image);
} }

View File

@ -1,7 +1,6 @@
//! Type safe native types for safe context/surface creation //! Type safe native types for safe context/surface creation
use super::{ffi, Error}; use super::{ffi, Error, EGLError, wrap_egl_call};
use crate::backend::graphics::SwapBuffersError;
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
use std::ptr; use std::ptr;
@ -28,7 +27,7 @@ pub trait Backend {
display: ffi::NativeDisplayType, display: ffi::NativeDisplayType,
has_dp_extension: F, has_dp_extension: F,
log: ::slog::Logger, log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay; ) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
@ -42,20 +41,20 @@ impl Backend for Wayland {
display: ffi::NativeDisplayType, display: ffi::NativeDisplayType,
has_dp_extension: F, has_dp_extension: F,
log: ::slog::Logger, log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,
{ {
if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() { if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland"); trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, ptr::null()))
} else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded() } else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
{ {
trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland"); trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland");
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_WAYLAND_EXT, display as *mut _, ptr::null()))
} else { } else {
trace!(log, "Default EGL Display Initialization via GetDisplay"); trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _) wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
} }
} }
} }
@ -74,19 +73,19 @@ impl Backend for X11 {
display: ffi::NativeDisplayType, display: ffi::NativeDisplayType,
has_dp_extension: F, has_dp_extension: F,
log: ::slog::Logger, log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay ) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where where
F: Fn(&str) -> bool, F: Fn(&str) -> bool,
{ {
if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() { if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11"); trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null()))
} else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() { } else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11"); trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null()) wrap_egl_call(|| ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null()))
} else { } else {
trace!(log, "Default EGL Display Initialization via GetDisplay"); trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _) wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
} }
} }
} }
@ -107,7 +106,7 @@ pub unsafe trait NativeDisplay<B: Backend> {
/// Return a raw pointer EGL will accept for context creation. /// Return a raw pointer EGL will accept for context creation.
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>; fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
/// Create a surface /// Create a surface
fn create_surface(&mut self, args: Self::Arguments) -> ::std::result::Result<B::Surface, Self::Error>; fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, Self::Error>;
} }
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
@ -166,6 +165,9 @@ 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 NativeSurface {
/// Error of the underlying surface
type Error: std::error::Error;
/// Return a raw pointer egl will accept for surface creation. /// Return a raw pointer egl will accept for surface creation.
fn ptr(&self) -> ffi::NativeWindowType; fn ptr(&self) -> ffi::NativeWindowType;
@ -184,21 +186,33 @@ pub unsafe trait NativeSurface {
/// Must only be implemented if `needs_recreation` can return `true`. /// Must only be implemented if `needs_recreation` can return `true`.
/// Returns true on success. /// Returns true on success.
/// If this call was successful `ptr()` *should* return something different. /// If this call was successful `ptr()` *should* return something different.
fn recreate(&self) -> bool { fn recreate(&self) -> Result<(), Self::Error> {
true Ok(())
} }
/// 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)
/// ///
/// Only implement if required by the backend. /// Only implement if required by the backend.
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> Result<(), Self::Error> {
Ok(()) Ok(())
} }
} }
/// Hack until ! gets stablized
#[derive(Debug)]
pub enum Never {}
impl std::fmt::Display for Never {
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { unreachable!() }
}
impl std::error::Error for Never {}
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for XlibWindow { unsafe impl NativeSurface for XlibWindow {
// this would really be a case for this:
// type Error = !; (https://github.com/rust-lang/rust/issues/35121)
type Error = Never;
fn ptr(&self) -> ffi::NativeWindowType { fn ptr(&self) -> ffi::NativeWindowType {
self.0 as *const _ self.0 as *const _
} }
@ -206,6 +220,9 @@ unsafe impl NativeSurface for XlibWindow {
#[cfg(feature = "backend_winit")] #[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface { unsafe impl NativeSurface for wegl::WlEglSurface {
// type Error = !;
type Error = Never;
fn ptr(&self) -> ffi::NativeWindowType { fn ptr(&self) -> ffi::NativeWindowType {
self.ptr() as *const _ self.ptr() as *const _
} }

View File

@ -1,8 +1,8 @@
//! EGL surface related structs //! EGL surface related structs
use super::{ffi, native, Error}; use super::{ffi, native, EGLError, SwapBuffersError, wrap_egl_call};
use crate::backend::egl::display::EGLDisplayHandle; use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::{PixelFormat, SwapBuffersError}; use crate::backend::graphics::PixelFormat;
use nix::libc::c_int; use nix::libc::c_int;
use std::sync::Arc; use std::sync::Arc;
use std::{ use std::{
@ -41,7 +41,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
config: ffi::egl::types::EGLConfig, config: ffi::egl::types::EGLConfig,
native: N, native: N,
log: L, log: L,
) -> Result<EGLSurface<N>, Error> ) -> Result<EGLSurface<N>, EGLError>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -68,13 +68,9 @@ impl<N: native::NativeSurface> EGLSurface<N> {
out out
}; };
let surface = unsafe { let surface = wrap_egl_call(|| unsafe {
ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr()) ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
}; })?;
if surface.is_null() {
return Err(Error::SurfaceCreationFailed);
}
Ok(EGLSurface { Ok(EGLSurface {
display, display,
@ -87,35 +83,34 @@ 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> { pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> {
let surface = self.surface.get(); let surface = self.surface.get();
if !surface.is_null() { let result = if !surface.is_null() {
let ret = unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) }; wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) })
.map_err(SwapBuffersError::EGLSwapBuffers)
.and_then(|_| self.native.swap_buffers().map_err(SwapBuffersError::Underlying))
} else { Ok(()) };
if ret == 0 { // workaround for missing `PartialEq` impl
match unsafe { ffi::egl::GetError() } as u32 { let is_bad_surface = if let Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface)) = result { true } else { false };
ffi::egl::CONTEXT_LOST => return Err(SwapBuffersError::ContextLost),
err => return Err(SwapBuffersError::Unknown(err)), if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
}; self.native.recreate().map_err(SwapBuffersError::Underlying)?;
} else { if !surface.is_null() {
self.native.swap_buffers()?; let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
} }
};
if self.native.needs_recreation() || surface.is_null() {
self.native.recreate();
self.surface.set(unsafe { self.surface.set(unsafe {
ffi::egl::CreateWindowSurface( wrap_egl_call(|| ffi::egl::CreateWindowSurface(
**self.display, **self.display,
self.config_id, self.config_id,
self.native.ptr(), self.native.ptr(),
self.surface_attributes.as_ptr(), self.surface_attributes.as_ptr(),
) )).map_err(SwapBuffersError::EGLCreateWindowSurface)?
}); });
} }
Ok(()) result
} }
/// Returns true if the OpenGL surface is the current one in the thread. /// Returns true if the OpenGL surface is the current one in the thread.

View File

@ -4,35 +4,30 @@ use crate::backend::graphics::{gl::GLGraphicsBackend, SwapBuffersError};
use glium::{ use glium::{
backend::{Backend, Context, Facade}, backend::{Backend, Context, Facade},
debug::DebugCallbackBehavior, debug::DebugCallbackBehavior,
Frame, SwapBuffersError as GliumSwapBuffersError, SwapBuffersError as GliumSwapBuffersError,
}; };
use std::{ use std::{
cell::{Ref, RefCell, RefMut}, cell::{Cell, Ref, RefCell, RefMut},
os::raw::c_void, os::raw::c_void,
rc::Rc, rc::Rc,
}; };
impl From<SwapBuffersError> for GliumSwapBuffersError {
fn from(error: SwapBuffersError) -> Self {
match error {
SwapBuffersError::ContextLost => GliumSwapBuffersError::ContextLost,
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
SwapBuffersError::Unknown(_) => GliumSwapBuffersError::ContextLost, // TODO
}
}
}
/// Wrapper to expose `Glium` compatibility /// Wrapper to expose `Glium` compatibility
pub struct GliumGraphicsBackend<T: GLGraphicsBackend> { pub struct GliumGraphicsBackend<T: GLGraphicsBackend> {
context: Rc<Context>, context: Rc<Context>,
backend: Rc<InternalBackend<T>>, backend: Rc<InternalBackend<T>>,
// at least this type is not `Send` or even `Sync`.
// while there can be multiple Frames, they cannot in parallel call `set_finish`.
// so a buffer of the last error is sufficient, if always cleared...
error_channel: Rc<Cell<Option<Box<dyn std::error::Error>>>>,
} }
struct InternalBackend<T: GLGraphicsBackend>(RefCell<T>); struct InternalBackend<T: GLGraphicsBackend>(RefCell<T>, Rc<Cell<Option<Box<dyn std::error::Error>>>>);
impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> { impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
fn new(backend: T) -> GliumGraphicsBackend<T> { fn new(backend: T) -> GliumGraphicsBackend<T> {
let internal = Rc::new(InternalBackend(RefCell::new(backend))); let error_channel = Rc::new(Cell::new(None));
let internal = Rc::new(InternalBackend(RefCell::new(backend), error_channel.clone()));
GliumGraphicsBackend { GliumGraphicsBackend {
// cannot fail // cannot fail
@ -40,6 +35,7 @@ impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
Context::new(internal.clone(), true, DebugCallbackBehavior::default()).unwrap() Context::new(internal.clone(), true, DebugCallbackBehavior::default()).unwrap()
}, },
backend: internal, backend: internal,
error_channel,
} }
} }
@ -51,7 +47,7 @@ impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
/// Note that destroying a [`Frame`] is immediate, even if vsync is enabled. /// Note that destroying a [`Frame`] is immediate, even if vsync is enabled.
#[inline] #[inline]
pub fn draw(&self) -> Frame { pub fn draw(&self) -> Frame {
Frame::new(self.context.clone(), self.backend.get_framebuffer_dimensions()) Frame(glium::Frame::new(self.context.clone(), self.backend.get_framebuffer_dimensions()), self.error_channel.clone())
} }
/// Borrow the underlying backend. /// Borrow the underlying backend.
@ -88,7 +84,20 @@ impl<T: GLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> { unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> {
fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> { fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> {
self.0.borrow().swap_buffers().map_err(Into::into) if let Err(err) = self.0.borrow().swap_buffers() {
Err(match err {
SwapBuffersError::ContextLost(err) => {
self.1.set(Some(err));
GliumSwapBuffersError::ContextLost
},
SwapBuffersError::TemporaryFailure(err) => {
self.1.set(Some(err));
GliumSwapBuffersError::AlreadySwapped
}
// I do not think, this may happen, but why not
SwapBuffersError::AlreadySwapped => GliumSwapBuffersError::AlreadySwapped,
})
} else { Ok(()) }
} }
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
@ -104,6 +113,94 @@ unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> {
} }
unsafe fn make_current(&self) { unsafe fn make_current(&self) {
// TODO, if this ever blows up anvil, we should probably silently ignore this.
// But I have no idea, if that may happen or what glium does, if the context is not current...
// So lets leave this in to do some real world testing
self.0.borrow().make_current().expect("Context was lost") self.0.borrow().make_current().expect("Context was lost")
} }
} }
/// Omplementation of `glium::Surface`, targeting the default framebuffer.
///
/// The back- and front-buffers are swapped when you call `finish`.
///
/// You **must** call either `finish` or `set_finish` or else the destructor will panic.
pub struct Frame(glium::Frame, Rc<Cell<Option<Box<dyn std::error::Error>>>>);
impl Frame {
/// Stop drawing, swap the buffers, and consume the Frame.
///
/// See the documentation of [`SwapBuffersError`] about what is being returned.
pub fn finish(mut self) -> Result<(), SwapBuffersError> {
self.set_finish()
}
/// Stop drawing, swap the buffers.
///
/// The Frame can now be dropped regularly. Calling `finish()` or `set_finish()` again will cause `Err(SwapBuffersError::AlreadySwapped)` to be returned.
pub fn set_finish(&mut self) -> Result<(), SwapBuffersError> {
let res = self.0.set_finish();
let err = self.1.take();
match (res, err) {
(Ok(()), _) => Ok(()),
(Err(GliumSwapBuffersError::AlreadySwapped), Some(err)) => Err(SwapBuffersError::TemporaryFailure(err)),
(Err(GliumSwapBuffersError::AlreadySwapped), None) => Err(SwapBuffersError::AlreadySwapped),
(Err(GliumSwapBuffersError::ContextLost), Some(err)) => Err(SwapBuffersError::ContextLost(err)),
_ => unreachable!(),
}
}
}
impl glium::Surface for Frame {
fn clear(&mut self, rect: Option<&glium::Rect>, color: Option<(f32, f32, f32, f32)>, color_srgb: bool,
depth: Option<f32>, stencil: Option<i32>)
{
self.0.clear(rect, color, color_srgb, depth, stencil)
}
fn get_dimensions(&self) -> (u32, u32) {
self.0.get_dimensions()
}
fn get_depth_buffer_bits(&self) -> Option<u16> {
self.0.get_depth_buffer_bits()
}
fn get_stencil_buffer_bits(&self) -> Option<u16> {
self.0.get_stencil_buffer_bits()
}
fn draw<'a, 'b, V, I, U>(&mut self, v: V, i: I, program: &glium::Program, uniforms: &U,
draw_parameters: &glium::draw_parameters::DrawParameters<'_>) -> Result<(), glium::DrawError> where
V: glium::vertex::MultiVerticesSource<'b>, I: Into<glium::index::IndicesSource<'a>>,
U: glium::uniforms::Uniforms
{
self.0.draw(v, i, program, uniforms, draw_parameters)
}
fn blit_from_frame(&self, source_rect: &glium::Rect, target_rect: &glium::BlitTarget,
filter: glium::uniforms::MagnifySamplerFilter)
{
self.0.blit_from_frame(source_rect, target_rect, filter);
}
fn blit_from_simple_framebuffer(&self, source: &glium::framebuffer::SimpleFrameBuffer<'_>,
source_rect: &glium::Rect, target_rect: &glium::BlitTarget,
filter: glium::uniforms::MagnifySamplerFilter)
{
self.0.blit_from_simple_framebuffer(source, source_rect, target_rect, filter)
}
fn blit_from_multioutput_framebuffer(&self, source: &glium::framebuffer::MultiOutputFrameBuffer<'_>,
source_rect: &glium::Rect, target_rect: &glium::BlitTarget,
filter: glium::uniforms::MagnifySamplerFilter)
{
self.0.blit_from_multioutput_framebuffer(source, source_rect, target_rect, filter)
}
fn blit_color<S>(&self, source_rect: &glium::Rect, target: &S, target_rect: &glium::BlitTarget,
filter: glium::uniforms::MagnifySamplerFilter) where S: glium::Surface
{
self.0.blit_color(source_rect, target, target_rect, filter)
}
}

View File

@ -16,24 +16,33 @@ pub mod glium;
pub mod software; pub mod software;
/// Error that can happen when swapping buffers. /// Error that can happen when swapping buffers.
#[derive(Debug, Clone, PartialEq, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum SwapBuffersError { pub enum SwapBuffersError {
/// The corresponding context has been lost and needs to be recreated.
///
/// All the objects associated to it (textures, buffers, programs, etc.)
/// need to be recreated from scratch.
///
/// Operations will have no effect. Functions that read textures, buffers, etc.
/// will return uninitialized data instead.
#[error("The context has been lost, it needs to be recreated")]
ContextLost,
/// The buffers have already been swapped. /// The buffers have already been swapped.
/// ///
/// This error can be returned when `swap_buffers` has been called multiple times /// This error can be returned when `swap_buffers` has been called multiple times
/// without any modification in between. /// without any modification in between.
#[error("Buffers are already swapped, swap_buffers was called too many times")] #[error("Buffers are already swapped, swap_buffers was called too many times")]
AlreadySwapped, AlreadySwapped,
/// Unknown error /// The corresponding context has been lost and needs to be recreated.
#[error("Unknown error: {0:x}")] ///
Unknown(u32), /// All the objects associated to it (textures, buffers, programs, etc.)
/// need to be recreated from scratch. Underlying resources like native surfaces
/// might also need to be recreated.
///
/// Operations will have no effect. Functions that read textures, buffers, etc.
/// will return uninitialized data instead.
#[error("The context has been lost, it needs to be recreated")]
ContextLost(Box<dyn std::error::Error>),
/// A temporary condition caused to rendering to fail.
///
/// Depending on the underlying error this *might* require fixing internal state of the rendering backend,
/// but failures mapped to `TemporaryFailure` are always recoverable without re-creating the entire stack,
/// as is represented by `ContextLost`.
///
/// Proceed after investigating the source to reschedule another full rendering step or just this page_flip at a later time.
/// If the root cause cannot be discovered and subsequent renderings also fail, it is advised to fallback to
/// recreation.
#[error("A temporary condition caused the page flip to fail.")]
TemporaryFailure(Box<dyn std::error::Error>),
} }

View File

@ -3,7 +3,7 @@
use crate::backend::egl::display::EGLDisplay; use crate::backend::egl::display::EGLDisplay;
use crate::backend::egl::get_proc_address; use crate::backend::egl::get_proc_address;
use crate::backend::{ use crate::backend::{
egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError}, egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError, SurfaceCreationError},
graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError}, graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError},
input::{ input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
@ -16,6 +16,7 @@ use nix::libc::c_void;
use std::{ use std::{
cell::{Ref, RefCell}, cell::{Ref, RefCell},
cmp, cmp,
convert::TryInto,
rc::Rc, rc::Rc,
time::Instant, time::Instant,
}; };
@ -47,6 +48,9 @@ pub enum Error {
/// EGL error /// EGL error
#[error("EGL error: {0}")] #[error("EGL error: {0}")]
EGL(#[from] EGLError), EGL(#[from] EGLError),
/// Surface Creation failed
#[error("Surface creation failed: {0}")]
SurfaceCreationError(#[from] SurfaceCreationError<EGLError>)
} }
enum Window { enum Window {
@ -277,8 +281,8 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Swapping buffers"); trace!(self.logger, "Swapping buffers");
match *self.window { match *self.window {
Window::Wayland { ref surface, .. } => surface.swap_buffers(), Window::Wayland { ref surface, .. } => surface.swap_buffers().map_err(|err| err.try_into().unwrap()),
Window::X11 { ref surface, .. } => surface.swap_buffers(), Window::X11 { ref surface, .. } => surface.swap_buffers().map_err(|err| err.try_into().unwrap()),
} }
} }
@ -314,12 +318,12 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
ref surface, ref surface,
ref context, ref context,
.. ..
} => context.make_current_with_surface(surface), } => context.make_current_with_surface(surface).map_err(Into::into),
Window::X11 { Window::X11 {
ref surface, ref surface,
ref context, ref context,
.. ..
} => context.make_current_with_surface(surface), } => context.make_current_with_surface(surface).map_err(Into::into),
} }
} }