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,
index::PrimitiveType,
texture::{MipmapsOption, Texture2d, UncompressedFloatFormat},
Frame, GlObject, Surface,
GlObject, Surface,
};
use slog::Logger;
@ -16,7 +16,7 @@ use smithay::backend::egl::display::EGLBufferReader;
use smithay::{
backend::{
egl::{BufferAccessError, EGLImages, Format},
graphics::{gl::GLGraphicsBackend, glium::GliumGraphicsBackend},
graphics::{gl::GLGraphicsBackend, glium::{GliumGraphicsBackend, Frame}},
},
reexports::wayland_server::protocol::{wl_buffer, wl_surface},
wayland::{
@ -159,7 +159,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() {
display.egl_buffer_contents(buffer)
} else {
Err(BufferAccessError::NotManaged(buffer))
Err(BufferAccessError::NotManaged(buffer, smithay::backend::egl::EGLError::BadDisplay))
};
match images {
Ok(images) => {
@ -193,7 +193,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
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
self.texture_from_shm_buffer(buffer)
}
@ -235,7 +235,7 @@ impl<F: GLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn render_texture(
&self,
target: &mut glium::Frame,
target: &mut Frame,
texture: &Texture2d,
texture_kind: usize,
y_inverted: bool,

View File

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

View File

@ -4,8 +4,8 @@
//!
use drm::control::{connector, crtc, Mode, RawResourceHandle};
use std::path::PathBuf;
use crate::backend::graphics::SwapBuffersError;
pub mod fallback;
@ -71,3 +71,21 @@ pub enum Error {
#[error("Atomic Test failed for new properties on crtc ({0:?})")]
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 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")]
use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
@ -33,8 +33,11 @@ pub mod session;
#[derive(thiserror::Error, Debug)]
pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> {
/// EGL Error
#[error("EGL error: {0:?}")]
#[error("EGL error: {0:}")]
EGL(#[source] EGLError),
/// EGL Error
#[error("EGL error: {0:}")]
RawEGL(#[source] RawEGLError),
/// Underlying backend error
#[error("Underlying backend error: {0:?}")]
Underlying(#[source] U),
@ -46,7 +49,7 @@ type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
pub struct EglDevice<B, D>
where
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,
{
dev: EGLDisplay<B, D>,
@ -58,7 +61,7 @@ where
impl<B, D> AsRawFd for EglDevice<B, D>
where
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,
{
fn as_raw_fd(&self) -> RawFd {
@ -69,7 +72,7 @@ where
impl<B, D> EglDevice<B, D>
where
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,
{
/// Try to create a new [`EglDevice`] from an open device.
@ -124,7 +127,7 @@ where
struct InternalDeviceHandler<B, D>
where
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,
{
handler: Box<dyn DeviceHandler<Device = EglDevice<B, D>> + 'static>,
@ -133,7 +136,7 @@ where
impl<B, D> DeviceHandler for InternalDeviceHandler<B, D>
where
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,
{
type Device = D;
@ -149,7 +152,7 @@ where
impl<B, D> Device for EglDevice<B, D>
where
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,
{
type Surface = EglSurface<<D as Device>::Surface>;
@ -188,7 +191,10 @@ where
context.get_config_id(),
(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 })
}
@ -225,7 +231,7 @@ where
impl<B, D> EGLGraphicsBackend for EglDevice<B, D>
where
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,
{
fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, EGLError> {
@ -236,7 +242,7 @@ where
impl<B, D> Drop for EglDevice<B, D>
where
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,
{
fn drop(&mut self) {

View File

@ -7,7 +7,7 @@ use drm::control::{crtc, connector, Mode};
use std::os::unix::io::RawFd;
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::session::{AsSessionObserver, SessionObserver};
@ -22,7 +22,7 @@ impl<S, B, D> AsSessionObserver<EglDeviceObserver<S>> for EglDevice<B, D>
where
S: SessionObserver + '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,
{
fn observer(&mut self) -> EglDeviceObserver<S> {

View File

@ -1,5 +1,6 @@
use drm::control::{connector, crtc, Mode};
use nix::libc::c_void;
use std::convert::TryInto;
use super::Error;
use crate::backend::drm::Surface;
@ -21,8 +22,8 @@ where
}
impl<N> Surface for EglSurface<N>
where
N: NativeSurface + Surface,
where
N: native::NativeSurface + Surface,
{
type Connectors = <N as Surface>::Connectors;
type Error = Error<<N as Surface>::Error>;
@ -90,9 +91,15 @@ where
impl<N> GLGraphicsBackend for EglSurface<N>
where
N: native::NativeSurface + Surface,
<N as NativeSurface>::Error: Into<SwapBuffersError> + 'static,
{
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 {
@ -109,7 +116,7 @@ where
}
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 {

View File

@ -7,8 +7,7 @@
use crate::backend::drm::{Device, RawDevice, Surface};
use crate::backend::egl::ffi;
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::Error as EglError;
use crate::backend::graphics::SwapBuffersError;
use crate::backend::egl::{Error as EglBackendError, EGLError, wrap_egl_call};
use super::{Error, GbmDevice, GbmSurface};
@ -31,22 +30,22 @@ impl<D: RawDevice + 'static> Backend for Gbm<D> {
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
where
F: Fn(&str) -> bool,
{
if has_dp_extension("EGL_KHR_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
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() {
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() {
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 {
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
}
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglError> {
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> {
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> {
type Error = Error<<<D as RawDevice>::Surface as Surface>::Error>;
fn ptr(&self) -> ffi::NativeWindowType {
self.0.surface.borrow().as_raw() as *const _
}
@ -77,25 +78,13 @@ unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
self.needs_recreation()
}
fn recreate(&self) -> bool {
if let Err(err) = GbmSurface::recreate(self) {
error!(self.0.logger, "Failure recreating internal resources: {}", err);
false
} else {
true
}
fn recreate(&self) -> Result<(), Self::Error> {
GbmSurface::recreate(self)
}
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
// if this is used by our egl module, which is why this trait is unsafe.
match 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))
}
}
unsafe { self.page_flip() }
}
}

View File

@ -10,6 +10,7 @@
//!
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::SystemError as DrmError;
@ -26,7 +27,7 @@ use std::sync::Once;
/// Errors thrown by the [`GbmDevice`](::backend::drm::gbm::GbmDevice)
/// and [`GbmSurface`](::backend::drm::gbm::GbmSurface).
#[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
#[error("Creation of GBM device failed")]
InitFailed(#[source] io::Error),
@ -256,3 +257,21 @@ impl<D: RawDevice + ControlDevice + 'static> Drop for GbmDevice<D> {
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
use super::{ffi, Error};
use super::{ffi, Error, MakeCurrentError, wrap_egl_call};
use crate::backend::egl::display::{EGLDisplay, EGLDisplayHandle};
use crate::backend::egl::native::NativeSurface;
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::ptr;
use std::sync::Arc;
@ -93,21 +93,14 @@ impl EGLContext {
trace!(log, "Creating EGL context...");
// TODO: Support shared contexts
let context = unsafe {
let context = wrap_egl_call(|| unsafe {
ffi::egl::CreateContext(
**display.display,
config_id,
ptr::null(),
context_attributes.as_ptr(),
)
};
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)),
}
}
}).map_err(Error::CreationFailed)?;
info!(log, "EGL context created");
@ -129,22 +122,13 @@ impl EGLContext {
pub unsafe fn make_current_with_surface<N>(
&self,
surface: &EGLSurface<N>,
) -> ::std::result::Result<(), SwapBuffersError>
) -> Result<(), MakeCurrentError>
where
N: NativeSurface,
{
let surface_ptr = surface.surface.get();
let ret = ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context);
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(())
}
wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context)).map(|_| ()).map_err(Into::into)
}
/// Makes the OpenGL context the current context in the current thread with no surface bound.
@ -152,18 +136,9 @@ impl EGLContext {
/// # Safety
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
let ret = ffi::egl::MakeCurrent(**self.display, ptr::null(), ptr::null(), self.context);
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(())
}
/// on multiple threads without being unbound again (see `unbind`)
pub unsafe fn make_current(&self) -> Result<(), MakeCurrentError> {
wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, ffi::egl::NO_SURFACE, ffi::egl::NO_SURFACE, self.context)).map(|_| ()).map_err(Into::into)
}
/// 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 {
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 {
fn drop(&mut self) {
unsafe {
// We need to ensure the context is unbound, otherwise it egl stalls the destroy call
if ffi::egl::GetCurrentContext() == self.context as *const _ {
ffi::egl::MakeCurrent(ptr::null(), ptr::null(), ptr::null(), ptr::null());
}
// ignore failures at this point
let _ = self.unbind();
ffi::egl::DestroyContext(**self.display, self.context);
}
}
@ -270,7 +253,7 @@ impl Default for PixelFormatRequirements {
impl PixelFormatRequirements {
/// 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 {
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated {
@ -328,7 +311,7 @@ impl PixelFormatRequirements {
if self.stereoscopy {
error!(logger, "Stereoscopy is currently unsupported (sorry!)");
return Err(Error::NoAvailablePixelFormat);
return Err(());
}
Ok(())

View File

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

View File

@ -1,3 +1,5 @@
use super::ffi;
#[derive(thiserror::Error, Debug)]
/// EGL errors
pub enum Error {
@ -5,8 +7,8 @@ pub enum Error {
#[error("The requested OpenGL version {0:?} is not supported")]
OpenGlVersionNotSupported((u8, u8)),
/// The EGL implementation does not support creating OpenGL ES contexts
#[error("The EGL implementation does not support creating OpenGL ES contexts")]
OpenGlesNotSupported,
#[error("The EGL implementation does not support creating OpenGL ES contexts. Err: {0:?}")]
OpenGlesNotSupported(#[source] Option<EGLError>),
/// No available pixel format matched the criteria
#[error("No available pixel format matched the criteria")]
NoAvailablePixelFormat,
@ -14,26 +16,23 @@ pub enum Error {
#[error("The expected backend '{0:?}' does not match the runtime")]
NonMatchingBackend(&'static str),
/// Unable to obtain a valid EGL Display
#[error("Unable to obtain a valid EGL Display")]
DisplayNotSupported,
#[error("Unable to obtain a valid EGL Display. Err: {0:}")]
DisplayNotSupported(#[source] EGLError),
/// `eglInitialize` returned an error
#[error("Failed to initialize EGL")]
InitFailed,
#[error("Failed to initialize EGL. Err: {0:}")]
InitFailed(#[source] EGLError),
/// 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
#[error("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements")]
CreationFailed,
/// `eglCreateWindowSurface` failed
#[error("`eglCreateWindowSurface` failed")]
SurfaceCreationFailed,
#[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(#[source] EGLError),
/// 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:?}")]
EglExtensionNotSupported(&'static [&'static str]),
/// 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`
#[error("No EGLDisplay is currently bound to this `WlDisplay`")]
NoEGLDisplayBound,
@ -43,7 +42,89 @@ pub enum Error {
/// Failed to create `EGLImages` from the buffer
#[error("Failed to create `EGLImages` from the buffer")]
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),
}
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.
#[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 std::fmt;
#[cfg(feature = "wayland_frontend")]
@ -28,7 +28,7 @@ use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
pub mod context;
pub use self::context::EGLContext;
mod error;
pub use self::error::Error;
pub use self::error::*;
use nix::libc::c_void;
@ -84,11 +84,11 @@ pub enum BufferAccessError {
#[error("The corresponding context was lost")]
ContextLost,
/// This buffer is not managed by the EGL buffer
#[error("This buffer is not managed by EGL")]
NotManaged(WlBuffer),
#[error("This buffer is not managed by EGL. Err: {1:}")]
NotManaged(WlBuffer, #[source] EGLError),
/// Failed to create `EGLImages` from the buffer
#[error("Failed to create EGLImages from the buffer")]
EGLImageCreationFailed,
#[error("Failed to create EGLImages from the buffer. Err: {0:}")]
EGLImageCreationFailed(#[source] EGLError),
/// The required EGL extension is not supported by the underlying EGL implementation
#[error("{0}")]
EglExtensionNotSupported(#[from] EglExtensionNotSupportedError),
@ -99,8 +99,8 @@ impl fmt::Debug for BufferAccessError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
match *self {
BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"),
BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"),
BufferAccessError::EGLImageCreationFailed => {
BufferAccessError::NotManaged(_, _) => write!(formatter, "BufferAccessError::NotManaged"),
BufferAccessError::EGLImageCreationFailed(_) => {
write!(formatter, "BufferAccessError::EGLImageCreationFailed")
}
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
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum TextureCreationError {
@ -249,6 +321,7 @@ impl EGLImages {
impl Drop for EGLImages {
fn drop(&mut self) {
for image in self.images.drain(..) {
// ignore result on drop
unsafe {
ffi::egl::DestroyImageKHR(**self.display, image);
}

View File

@ -1,7 +1,6 @@
//! Type safe native types for safe context/surface creation
use super::{ffi, Error};
use crate::backend::graphics::SwapBuffersError;
use super::{ffi, Error, EGLError, wrap_egl_call};
#[cfg(feature = "backend_winit")]
use std::ptr;
@ -28,7 +27,7 @@ pub trait Backend {
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay;
) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
}
#[cfg(feature = "backend_winit")]
@ -42,20 +41,20 @@ impl Backend for Wayland {
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
) -> 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");
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()
{
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 {
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,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay
) -> 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");
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() {
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 {
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.
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
/// 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")]
@ -166,6 +165,9 @@ 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 {
/// Error of the underlying surface
type Error: std::error::Error;
/// Return a raw pointer egl will accept for surface creation.
fn ptr(&self) -> ffi::NativeWindowType;
@ -184,21 +186,33 @@ pub unsafe trait NativeSurface {
/// Must only be implemented if `needs_recreation` can return `true`.
/// Returns true on success.
/// If this call was successful `ptr()` *should* return something different.
fn recreate(&self) -> bool {
true
fn recreate(&self) -> Result<(), Self::Error> {
Ok(())
}
/// Adds additional semantics when calling
/// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
///
/// Only implement if required by the backend.
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
fn swap_buffers(&self) -> Result<(), Self::Error> {
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")]
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 {
self.0 as *const _
}
@ -206,7 +220,10 @@ unsafe impl NativeSurface for XlibWindow {
#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface {
// type Error = !;
type Error = Never;
fn ptr(&self) -> ffi::NativeWindowType {
self.ptr() as *const _
}
}
}

View File

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

View File

@ -4,35 +4,30 @@ use crate::backend::graphics::{gl::GLGraphicsBackend, SwapBuffersError};
use glium::{
backend::{Backend, Context, Facade},
debug::DebugCallbackBehavior,
Frame, SwapBuffersError as GliumSwapBuffersError,
SwapBuffersError as GliumSwapBuffersError,
};
use std::{
cell::{Ref, RefCell, RefMut},
cell::{Cell, Ref, RefCell, RefMut},
os::raw::c_void,
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
pub struct GliumGraphicsBackend<T: GLGraphicsBackend> {
context: Rc<Context>,
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> {
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 {
// cannot fail
@ -40,6 +35,7 @@ impl<T: GLGraphicsBackend + 'static> GliumGraphicsBackend<T> {
Context::new(internal.clone(), true, DebugCallbackBehavior::default()).unwrap()
},
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.
#[inline]
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.
@ -88,7 +84,20 @@ impl<T: GLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> {
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 {
@ -104,6 +113,94 @@ unsafe impl<T: GLGraphicsBackend> Backend for InternalBackend<T> {
}
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")
}
}
/// 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;
/// Error that can happen when swapping buffers.
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
#[derive(Debug, thiserror::Error)]
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.
///
/// This error can be returned when `swap_buffers` has been called multiple times
/// without any modification in between.
#[error("Buffers are already swapped, swap_buffers was called too many times")]
AlreadySwapped,
/// Unknown error
#[error("Unknown error: {0:x}")]
Unknown(u32),
/// 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. 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::get_proc_address;
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},
input::{
Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent,
@ -16,6 +16,7 @@ use nix::libc::c_void;
use std::{
cell::{Ref, RefCell},
cmp,
convert::TryInto,
rc::Rc,
time::Instant,
};
@ -47,6 +48,9 @@ pub enum Error {
/// EGL error
#[error("EGL error: {0}")]
EGL(#[from] EGLError),
/// Surface Creation failed
#[error("Surface creation failed: {0}")]
SurfaceCreationError(#[from] SurfaceCreationError<EGLError>)
}
enum Window {
@ -277,8 +281,8 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Swapping buffers");
match *self.window {
Window::Wayland { ref surface, .. } => surface.swap_buffers(),
Window::X11 { 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().map_err(|err| err.try_into().unwrap()),
}
}
@ -314,12 +318,12 @@ impl GLGraphicsBackend for WinitGraphicsBackend {
ref surface,
ref context,
..
} => context.make_current_with_surface(surface),
} => context.make_current_with_surface(surface).map_err(Into::into),
Window::X11 {
ref surface,
ref context,
..
} => context.make_current_with_surface(surface),
} => context.make_current_with_surface(surface).map_err(Into::into),
}
}