Prevent display being destroyed until all resources have been dropped

This commit is contained in:
Chandler Newman 2020-04-16 13:44:32 +01:00
parent c4016af67b
commit f8c97baf1d
4 changed files with 229 additions and 258 deletions

View File

@ -1,17 +1,17 @@
//! EGL context related structs
use super::{ffi, Error};
use crate::backend::egl::display::EGLDisplay;
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 std::ptr;
use std::sync::{Arc, Weak};
use std::sync::Arc;
/// EGL context for rendering
pub struct EGLContext {
context: Arc<ffi::egl::types::EGLContext>,
display: Weak<ffi::egl::types::EGLDisplay>,
context: ffi::egl::types::EGLContext,
display: Arc<EGLDisplayHandle>,
config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat,
}
@ -94,7 +94,7 @@ impl EGLContext {
// TODO: Support shared contexts
let context = unsafe {
ffi::egl::CreateContext(
*display.display,
**display.display,
config_id,
ptr::null(),
context_attributes.as_ptr(),
@ -111,8 +111,8 @@ impl EGLContext {
info!(log, "EGL context created");
Ok(EGLContext {
context: Arc::new(context as _),
display: Arc::downgrade(&display.display),
context,
display: display.display.clone(),
config_id,
pixel_format,
})
@ -132,26 +132,17 @@ impl EGLContext {
where
N: NativeSurface,
{
if let Some(display) = self.display.upgrade() {
let surface_ptr = surface.surface.get();
let surface_ptr = surface.surface.get();
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
surface_ptr as *const _,
surface_ptr as *const _,
(*self.context) as *const _,
);
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(())
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 {
Err(SwapBuffersError::ContextLost)
Ok(())
}
}
@ -162,32 +153,21 @@ impl EGLContext {
/// 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> {
if let Some(display) = self.display.upgrade() {
let surface_ptr = ptr::null();
let ret = ffi::egl::MakeCurrent(**self.display, ptr::null(), ptr::null(), self.context);
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
surface_ptr as *const _,
surface_ptr as *const _,
(*self.context) as *const _,
);
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(())
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 {
Err(SwapBuffersError::ContextLost)
Ok(())
}
}
/// Returns true if the OpenGL context is the current one in the thread.
pub fn is_current(&self) -> bool {
unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ }
unsafe { ffi::egl::GetCurrentContext() == self.context as *const _ }
}
/// Returns the egl config for this context
@ -204,11 +184,12 @@ impl EGLContext {
impl Drop for EGLContext {
fn drop(&mut self) {
unsafe {
// we don't call MakeCurrent(0, 0) because we are not sure that the context
// is still the current one
if let Some(display) = self.display.upgrade() {
ffi::egl::DestroyContext((*display) as *const _, (*self.context) as *const _);
// 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());
}
ffi::egl::DestroyContext(**self.display, self.context);
}
}
}

View File

@ -5,7 +5,7 @@ use crate::backend::egl::EGLGraphicsBackend;
use crate::backend::egl::{
ffi, get_proc_address, native, BufferAccessError, EGLContext, EGLImages, EGLSurface, Error, Format,
};
use std::sync::{Arc, Weak};
use std::sync::Arc;
use std::ptr;
@ -25,10 +25,34 @@ use std::ffi::{CStr, CString};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::Deref;
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
/// once all resources bound to it have been dropped.
pub(crate) struct EGLDisplayHandle {
handle: ffi::egl::types::EGLDisplay,
}
impl Deref for EGLDisplayHandle {
type Target = ffi::egl::types::EGLDisplay;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl Drop for EGLDisplayHandle {
fn drop(&mut self) {
unsafe {
ffi::egl::Terminate(self.handle);
}
}
}
/// [`EGLDisplay`] represents an initialised EGL environment
pub struct EGLDisplay<B: native::Backend, N: native::NativeDisplay<B>> {
native: RefCell<N>,
pub(crate) display: Arc<ffi::egl::types::EGLDisplay>,
pub(crate) display: Arc<EGLDisplayHandle>,
pub(crate) egl_version: (i32, i32),
pub(crate) extensions: Vec<String>,
logger: slog::Logger,
@ -126,7 +150,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
Ok(EGLDisplay {
native: RefCell::new(native),
display: Arc::new(display as *const _),
display: Arc::new(EGLDisplayHandle { handle: display }),
egl_version,
extensions,
logger: log,
@ -259,7 +283,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
let mut num_configs = MaybeUninit::uninit();
if unsafe {
ffi::egl::ChooseConfig(
*self.display,
**self.display,
descriptor.as_ptr(),
config_id.as_mut_ptr(),
1,
@ -285,7 +309,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
($display:expr, $config:expr, $attr:expr) => {{
let mut value = MaybeUninit::uninit();
let res = ffi::egl::GetConfigAttrib(
*$display,
**$display,
$config,
$attr as ffi::egl::types::EGLint,
value.as_mut_ptr(),
@ -345,7 +369,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
})?;
EGLSurface::new(
&self.display,
self.display.clone(),
pixel_format,
double_buffer,
config,
@ -388,14 +412,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
}
}
impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLDisplay<B, N> {
fn drop(&mut self) {
unsafe {
ffi::egl::Terminate((*self.display) as *const _);
}
}
}
#[cfg(feature = "use_system_lib")]
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGLDisplay<B, N> {
/// Binds this EGL display to the given Wayland display.
@ -414,12 +430,12 @@ 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 _) };
let res = unsafe { ffi::egl::BindWaylandDisplayWL(**self.display, display.c_ptr() as *mut _) };
if res == 0 {
return Err(Error::OtherEGLDisplayAlreadyBound);
}
Ok(EGLBufferReader::new(
Arc::downgrade(&self.display),
self.display.clone(),
display.c_ptr(),
&self.extensions,
))
@ -431,7 +447,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGL
/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`].
#[cfg(feature = "use_system_lib")]
pub struct EGLBufferReader {
display: Weak<ffi::egl::types::EGLDisplay>,
display: Arc<EGLDisplayHandle>,
wayland: *mut wl_display,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
@ -441,11 +457,7 @@ pub struct EGLBufferReader {
#[cfg(feature = "use_system_lib")]
impl EGLBufferReader {
fn new(
display: Weak<ffi::egl::types::EGLDisplay>,
wayland: *mut wl_display,
extensions: &[String],
) -> Self {
fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display, extensions: &[String]) -> Self {
#[cfg(feature = "renderer_gl")]
let gl = gl_ffi::Gles2::load_with(|s| get_proc_address(s) as *const _);
@ -470,105 +482,101 @@ impl EGLBufferReader {
&self,
buffer: WlBuffer,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.display.upgrade() {
let mut format: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::EGL_TEXTURE_FORMAT,
&mut format as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
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,
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV,
ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V,
ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV,
_ => panic!("EGL returned invalid texture type"),
};
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return Err(BufferAccessError::NotManaged(buffer));
}
let mut inverted: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted as *mut _,
) != 0
} {
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() {
let mut out = Vec::with_capacity(3);
out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
out.push(i as i32);
out.push(ffi::egl::NONE as i32);
images.push({
let image = unsafe {
ffi::egl::CreateImageKHR(
*display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.as_ref().c_ptr() as *mut _,
out.as_ptr(),
)
};
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: Arc::downgrade(&display),
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format,
images,
buffer,
#[cfg(feature = "renderer_gl")]
gl: self.gl.clone(),
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: self.egl_to_texture_support,
})
} else {
Err(BufferAccessError::ContextLost)
let mut format: i32 = 0;
if 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));
}
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,
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV,
ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V,
ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV,
_ => panic!("EGL returned invalid texture type"),
};
let mut width: i32 = 0;
if 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));
}
let mut height: i32 = 0;
if 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));
}
let mut inverted: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.as_ref().c_ptr() as _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted,
) != 0
} {
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() {
let mut out = Vec::with_capacity(3);
out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
out.push(i as i32);
out.push(ffi::egl::NONE as i32);
images.push({
let image = unsafe {
ffi::egl::CreateImageKHR(
**self.display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.as_ref().c_ptr() as *mut _,
out.as_ptr(),
)
};
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: self.display.clone(),
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format,
images,
buffer,
#[cfg(feature = "renderer_gl")]
gl: self.gl.clone(),
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: self.egl_to_texture_support,
})
}
/// Try to receive the dimensions of a given [`WlBuffer`].
@ -576,46 +584,40 @@ impl EGLBufferReader {
/// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) or the
/// context has been lost, `None` is returned.
pub fn egl_buffer_dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> {
if let Some(display) = self.display.upgrade() {
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::WIDTH as i32,
&mut width as *mut _,
) == 0
} {
return None;
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.as_ref().c_ptr() as *mut _,
ffi::egl::HEIGHT as i32,
&mut height as *mut _,
) == 0
} {
return None;
}
Some((width, height))
} else {
None
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.as_ref().c_ptr() as _,
ffi::egl::WIDTH as _,
&mut width,
) == 0
} {
return None;
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.as_ref().c_ptr() as _,
ffi::egl::HEIGHT as _,
&mut height,
) == 0
} {
return None;
}
Some((width, height))
}
}
#[cfg(feature = "use_system_lib")]
impl Drop for EGLBufferReader {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
if !self.wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _);
}
if !self.wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _);
}
}
}

View File

@ -42,8 +42,9 @@ pub mod surface;
pub use self::surface::EGLSurface;
#[cfg(feature = "use_system_lib")]
use crate::backend::egl::display::EGLBufferReader;
use crate::backend::egl::display::EGLDisplayHandle;
use std::ffi::CString;
use std::sync::Weak;
use std::sync::Arc;
/// Error that can happen on optional EGL features
#[derive(Debug, Clone, PartialEq)]
@ -167,7 +168,7 @@ impl Format {
/// Images of the EGL-based [`WlBuffer`].
#[cfg(feature = "wayland_frontend")]
pub struct EGLImages {
display: Weak<ffi::egl::types::EGLDisplay>,
display: Arc<EGLDisplayHandle>,
/// Width in pixels
pub width: u32,
/// Height in pixels
@ -204,41 +205,35 @@ impl EGLImages {
plane: usize,
tex_id: c_uint,
) -> ::std::result::Result<(), TextureCreationError> {
if self.display.upgrade().is_some() {
if !self.egl_to_texture_support {
return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
}
let mut old_tex_id: i32 = 0;
self.gl.GetIntegerv(gl_ffi::TEXTURE_BINDING_2D, &mut old_tex_id);
self.gl.BindTexture(gl_ffi::TEXTURE_2D, tex_id);
self.gl.EGLImageTargetTexture2DOES(
gl_ffi::TEXTURE_2D,
*self
.images
.get(plane)
.ok_or(TextureCreationError::PlaneIndexOutOfBounds)?,
);
let res = match ffi::egl::GetError() as u32 {
ffi::egl::SUCCESS => Ok(()),
err => Err(TextureCreationError::TextureBindingFailed(err)),
};
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
res
} else {
Err(TextureCreationError::ContextLost)
if !self.egl_to_texture_support {
return Err(TextureCreationError::GLExtensionNotSupported("GL_OES_EGL_image"));
}
let mut old_tex_id: i32 = 0;
self.gl.GetIntegerv(gl_ffi::TEXTURE_BINDING_2D, &mut old_tex_id);
self.gl.BindTexture(gl_ffi::TEXTURE_2D, tex_id);
self.gl.EGLImageTargetTexture2DOES(
gl_ffi::TEXTURE_2D,
*self
.images
.get(plane)
.ok_or(TextureCreationError::PlaneIndexOutOfBounds)?,
);
let res = match ffi::egl::GetError() as u32 {
ffi::egl::SUCCESS => Ok(()),
err => Err(TextureCreationError::TextureBindingFailed(err)),
};
self.gl.BindTexture(gl_ffi::TEXTURE_2D, old_tex_id as u32);
res
}
}
#[cfg(feature = "wayland_frontend")]
impl Drop for EGLImages {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
for image in self.images.drain(..) {
unsafe {
ffi::egl::DestroyImageKHR(*display, image);
}
for image in self.images.drain(..) {
unsafe {
ffi::egl::DestroyImageKHR(**self.display, image);
}
}
self.buffer.release();

View File

@ -1,9 +1,10 @@
//! EGL surface related structs
use super::{ffi, native, Error};
use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::{PixelFormat, SwapBuffersError};
use nix::libc::c_int;
use std::sync::{Arc, Weak};
use std::sync::Arc;
use std::{
cell::Cell,
ops::{Deref, DerefMut},
@ -11,7 +12,7 @@ use std::{
/// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> {
display: Weak<ffi::egl::types::EGLDisplay>,
display: Arc<EGLDisplayHandle>,
native: N,
pub(crate) surface: Cell<ffi::egl::types::EGLSurface>,
config_id: ffi::egl::types::EGLConfig,
@ -34,7 +35,7 @@ impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<L>(
display: &Arc<ffi::egl::types::EGLDisplay>,
display: Arc<EGLDisplayHandle>,
pixel_format: PixelFormat,
double_buffered: Option<bool>,
config: ffi::egl::types::EGLConfig,
@ -76,7 +77,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
}
Ok(EGLSurface {
display: Arc::downgrade(display),
display,
native,
surface: Cell::new(surface),
config_id: config,
@ -90,34 +91,28 @@ impl<N: native::NativeSurface> EGLSurface<N> {
let surface = self.surface.get();
if !surface.is_null() {
if let Some(display) = self.display.upgrade() {
let ret = unsafe { ffi::egl::SwapBuffers((*display) as *const _, surface as *const _) };
let ret = unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) };
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()?;
}
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 {
return Err(SwapBuffersError::ContextLost);
self.native.swap_buffers()?;
}
};
if self.native.needs_recreation() || surface.is_null() {
if let Some(display) = self.display.upgrade() {
self.native.recreate();
self.surface.set(unsafe {
ffi::egl::CreateWindowSurface(
*display,
self.config_id,
self.native.ptr(),
self.surface_attributes.as_ptr(),
)
});
}
self.native.recreate();
self.surface.set(unsafe {
ffi::egl::CreateWindowSurface(
**self.display,
self.config_id,
self.native.ptr(),
self.surface_attributes.as_ptr(),
)
});
}
Ok(())
@ -144,10 +139,8 @@ impl<N: native::NativeSurface> EGLSurface<N> {
impl<N: native::NativeSurface> Drop for EGLSurface<N> {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
unsafe {
ffi::egl::DestroySurface((*display) as *const _, self.surface.get() as *const _);
}
unsafe {
ffi::egl::DestroySurface(**self.display, self.surface.get() as *const _);
}
}
}