#[cfg(feature = "renderer_gl")]
use crate::backend::graphics::gl::ffi as gl_ffi;
use nix::libc::c_uint;
use std::{
ffi::CStr,
fmt,
rc::{Rc, Weak},
};
use wayland_server::{
protocol::wl_buffer::{self, WlBuffer},
Display, Resource,
};
#[cfg(feature = "native_lib")]
use wayland_sys::server::wl_display;
pub mod context;
pub use self::context::EGLContext;
pub mod error;
use self::error::*;
#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
pub mod ffi;
use self::ffi::egl::types::EGLImage;
pub mod native;
pub mod surface;
pub use self::surface::EGLSurface;
#[derive(Debug, Clone, PartialEq)]
pub struct EglExtensionNotSupportedError(&'static [&'static str]);
impl fmt::Display for EglExtensionNotSupportedError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
write!(
formatter,
"None of the following EGL extensions is supported by the underlying EGL implementation,
at least one is required: {:?}",
self.0
)
}
}
impl ::std::error::Error for EglExtensionNotSupportedError {
fn description(&self) -> &str {
"The required EGL extension is not supported by the underlying EGL implementation"
}
fn cause(&self) -> Option<&dyn ::std::error::Error> {
None
}
}
pub enum BufferAccessError {
ContextLost,
NotManaged(Resource<WlBuffer>),
EGLImageCreationFailed,
EglExtensionNotSupported(EglExtensionNotSupportedError),
}
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 => {
write!(formatter, "BufferAccessError::EGLImageCreationFailed")
}
BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err),
}
}
}
impl fmt::Display for BufferAccessError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
use std::error::Error;
match *self {
BufferAccessError::ContextLost
| BufferAccessError::NotManaged(_)
| BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()),
BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter),
}
}
}
impl ::std::error::Error for BufferAccessError {
fn description(&self) -> &str {
match *self {
BufferAccessError::ContextLost => "The corresponding context was lost",
BufferAccessError::NotManaged(_) => "This buffer is not managed by EGL",
BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer",
BufferAccessError::EglExtensionNotSupported(ref err) => err.description(),
}
}
fn cause(&self) -> Option<&dyn ::std::error::Error> {
match *self {
BufferAccessError::EglExtensionNotSupported(ref err) => Some(err),
_ => None,
}
}
}
impl From<EglExtensionNotSupportedError> for BufferAccessError {
fn from(error: EglExtensionNotSupportedError) -> Self {
BufferAccessError::EglExtensionNotSupported(error)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TextureCreationError {
PlaneIndexOutOfBounds,
ContextLost,
GLExtensionNotSupported(&'static str),
TextureBindingFailed(u32),
}
impl fmt::Display for TextureCreationError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
use std::error::Error;
match *self {
TextureCreationError::ContextLost => write!(formatter, "{}", self.description()),
TextureCreationError::PlaneIndexOutOfBounds => write!(formatter, "{}", self.description()),
TextureCreationError::GLExtensionNotSupported(ext) => {
write!(formatter, "{}: {:}", self.description(), ext)
}
TextureCreationError::TextureBindingFailed(code) => {
write!(formatter, "{}. Gl error code: {:?}", self.description(), code)
}
}
}
}
impl ::std::error::Error for TextureCreationError {
fn description(&self) -> &str {
match *self {
TextureCreationError::ContextLost => "The context has been lost, it needs to be recreated",
TextureCreationError::PlaneIndexOutOfBounds => "This buffer is not managed by EGL",
TextureCreationError::GLExtensionNotSupported(_) => {
"Required OpenGL Extension for texture creation is missing"
}
TextureCreationError::TextureBindingFailed(_) => "Failed to create EGLImages from the buffer",
}
}
fn cause(&self) -> Option<&dyn ::std::error::Error> {
None
}
}
#[repr(i32)]
#[allow(non_camel_case_types)]
#[derive(Debug)]
pub enum Format {
RGB = ffi::egl::TEXTURE_RGB as i32,
RGBA = ffi::egl::TEXTURE_RGBA as i32,
External = ffi::egl::TEXTURE_EXTERNAL_WL,
Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
}
impl Format {
pub fn num_planes(&self) -> usize {
match *self {
Format::RGB | Format::RGBA | Format::External => 1,
Format::Y_UV | Format::Y_XUXV => 2,
Format::Y_U_V => 3,
}
}
}
pub struct EGLImages {
display: Weak<ffi::egl::types::EGLDisplay>,
pub width: u32,
pub height: u32,
pub y_inverted: bool,
pub format: Format,
images: Vec<EGLImage>,
buffer: Resource<WlBuffer>,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: bool,
}
impl EGLImages {
pub fn num_planes(&self) -> usize {
self.format.num_planes()
}
#[cfg(feature = "renderer_gl")]
pub unsafe fn bind_to_texture(
&self,
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)
}
}
}
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);
}
}
}
self.buffer.send(wl_buffer::Event::Release);
}
}
#[cfg(feature = "native_lib")]
pub trait EGLGraphicsBackend {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay>;
}
#[cfg(feature = "native_lib")]
pub struct EGLDisplay {
egl: Weak<ffi::egl::types::EGLDisplay>,
wayland: *mut wl_display,
#[cfg(feature = "renderer_gl")]
gl: gl_ffi::Gles2,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: bool,
}
#[cfg(feature = "native_lib")]
impl EGLDisplay {
fn new<B: native::Backend, N: native::NativeDisplay<B>>(
context: &EGLContext<B, N>,
display: *mut wl_display,
) -> EGLDisplay {
#[cfg(feature = "renderer_gl")]
let gl = gl_ffi::Gles2::load_with(|s| unsafe { context.get_proc_address(s) as *const _ });
EGLDisplay {
egl: Rc::downgrade(&context.display),
wayland: display,
#[cfg(feature = "renderer_gl")]
egl_to_texture_support: {
let data = unsafe { CStr::from_ptr(gl.GetString(gl_ffi::EXTENSIONS) as *const _) }
.to_bytes()
.to_vec();
let list = String::from_utf8(data).unwrap();
list.split(' ')
.any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base")
},
#[cfg(feature = "renderer_gl")]
gl,
}
}
pub fn egl_buffer_contents(
&self,
buffer: Resource<WlBuffer>,
) -> ::std::result::Result<EGLImages, BufferAccessError> {
if let Some(display) = self.egl.upgrade() {
let mut format: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
*display,
buffer.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.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.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.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.c_ptr() as *mut _,
out.as_ptr(),
)
};
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: Rc::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)
}
}
}
#[cfg(feature = "native_lib")]
impl Drop for EGLDisplay {
fn drop(&mut self) {
if let Some(display) = self.egl.upgrade() {
if !self.wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(*display, self.wayland as *mut _);
}
}
}
}
}
#[cfg(feature = "native_lib")]
impl<E: EGLGraphicsBackend> EGLGraphicsBackend for Rc<E> {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
(**self).bind_wl_display(display)
}
}
#[cfg(feature = "native_lib")]
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLGraphicsBackend for EGLContext<B, N> {
fn bind_wl_display(&self, display: &Display) -> Result<EGLDisplay> {
if !self.wl_drm_support {
bail!(ErrorKind::EglExtensionNotSupported(&[
"EGL_WL_bind_wayland_display"
]));
}
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.c_ptr() as *mut _) };
if res == 0 {
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
}
Ok(EGLDisplay::new(self, display.c_ptr()))
}
}