diff --git a/src/backend/graphics/egl/context.rs b/src/backend/graphics/egl/context.rs index 236c132..a09053f 100644 --- a/src/backend/graphics/egl/context.rs +++ b/src/backend/graphics/egl/context.rs @@ -18,7 +18,6 @@ use std::mem; use std::ops::{Deref, DerefMut}; #[cfg(feature = "backend_drm")] use std::os::unix::io::{AsRawFd, RawFd}; -use wayland_server::Display; /// EGL context for rendering pub struct EGLContext> { @@ -28,8 +27,8 @@ pub struct EGLContext> { pub(crate) config_id: ffi::egl::types::EGLConfig, pub(crate) surface_attributes: Vec, pixel_format: PixelFormat, - wl_drm_support: bool, - egl_to_texture_support: bool, + pub(crate) wl_drm_support: bool, + pub(crate) egl_to_texture_support: bool, logger: slog::Logger, _backend: PhantomData, } @@ -474,68 +473,6 @@ impl> EGLContext { pub fn get_pixel_format(&self) -> PixelFormat { self.pixel_format } - - /// Binds this EGL context to the given Wayland display. - /// - /// This will allow clients to utilize EGL to create hardware-accelerated - /// surfaces. The server will need to be able to handle egl-wl_buffers. - /// See the `wayland::drm` module. - /// - /// ## Errors - /// - /// This might return `WlExtensionNotSupported` if binding is not supported - /// by the EGL implementation. - /// - /// This might return `OtherEGLDisplayAlreadyBound` if called for the same - /// `Display` multiple times, as only one context may be bound at any given time. - pub fn bind_wl_display(&self, display: &Display) -> Result<()> { - if !self.wl_drm_support { - bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); - } - let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; - if res == 0 { - bail!(ErrorKind::OtherEGLDisplayAlreadyBound); - } - Ok(()) - } - - /// Unbinds this EGL context from the given Wayland display. - /// - /// This will stop clients from using previously available extensions - /// to utilize hardware-accelerated surface via EGL. - /// - /// ## Errors - /// - /// This might return `WlExtensionNotSupported` if binding is not supported - /// by the EGL implementation. - /// - /// This might return `OtherEGLDisplayAlreadyBound` if called for the same - /// `Display` multiple times, as only one context may be bound at any given time. - pub fn unbind_wl_display(&self, display: &Display) -> Result<()> { - if !self.wl_drm_support { - bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); - } - let res = unsafe { ffi::egl::UnbindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; - if res == 0 { - bail!(ErrorKind::NoEGLDisplayBound); - } - Ok(()) - } - - /* - pub unsafe fn egl_image_to_texture(&self, image: ffi::egl::types::EGLImage, tex_id: c_uint) -> Result<()> { - if !self.egl_to_texture_support { - bail!(ErrorKind::EglExtensionNotSupported(&["EGL_OES_image", "EGL_OES_image_base"])); - } - ffi::gl::EGLImageTargetTexture2DOES(tex_id, image); - Ok(()) - } - - pub unsafe fn destroy_egl_image(&self, image: ffi::egl::types::EGLImage, tex_id: c_uint) -> Result<()> { - ffi::gl::DestroyImageKHR(self.display, image); - Ok(()) - } - */ } unsafe impl + Send> Send for EGLContext {} diff --git a/src/backend/graphics/egl/error.rs b/src/backend/graphics/egl/error.rs index 756ae56..9775cf9 100644 --- a/src/backend/graphics/egl/error.rs +++ b/src/backend/graphics/egl/error.rs @@ -47,6 +47,11 @@ error_chain! { description("Failed to create a new EGLSurface") } + #[doc = "The OpenGL context has been lost and needs to be recreated"] + ContextLost { + description("The OpenGL context has been lost and needs to be recreated") + } + #[doc = "The required EGL extension is not supported by the underlying EGL implementation"] EglExtensionNotSupported(extensions: &'static [&'static str]) { description("The required EGL extension is not supported by the underlying EGL implementation"), @@ -64,6 +69,21 @@ error_chain! { description("No EGLDisplay is currently bound to this WlDisplay") } + #[doc = "Index of plane is out of bounds for EGLImages"] + PlaneIndexOutOfBounds { + description("Index of plane is out of bounds for EGLImages") + } + + #[doc = "This buffer is not mananged by EGL"] + BufferNotManaged { + description("This buffer is not mananged by EGL") + } + + #[doc = "Failed to create EGLImages from the buffer"] + EGLImageCreationFailed { + description("Failed to create EGLImages from the buffer") + } + #[doc = "The reason of failure could not be determined"] Unknown(err_no: u32) } diff --git a/src/backend/graphics/egl/mod.rs b/src/backend/graphics/egl/mod.rs index c12608f..2c2ca33 100644 --- a/src/backend/graphics/egl/mod.rs +++ b/src/backend/graphics/egl/mod.rs @@ -19,6 +19,7 @@ pub mod ffi; pub mod native; pub mod surface; pub use self::surface::EGLSurface; +pub mod wayland; /// Error that can happen when swapping buffers. #[derive(Debug, Clone, PartialEq)] @@ -166,6 +167,7 @@ pub trait EGLGraphicsBackend: GraphicsBackend { /// This might return `OtherEGLDisplayAlreadyBound` if called for the same /// `Display` multiple times, as only one context may be bound at any given time. fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>; + /// Unbinds this EGL context from the given Wayland display. /// /// This will stop clients from using previously available extensions diff --git a/src/backend/graphics/egl/wayland.rs b/src/backend/graphics/egl/wayland.rs new file mode 100644 index 0000000..8874018 --- /dev/null +++ b/src/backend/graphics/egl/wayland.rs @@ -0,0 +1,182 @@ +use backend::graphics::egl::{EGLContext, EGLImage, ffi, native}; +use backend::graphics::egl::error::*; +use nix::libc::{c_uint}; +use std::rc::{Rc, Weak}; +use wayland_server::{Display, Resource}; +use wayland_server::protocol::wl_buffer::WlBuffer; + +#[repr(i32)] +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, + pub width: u32, + pub height: u32, + pub y_inverted: bool, + pub format: Format, + images: Vec, + buffer: WlBuffer, +} + +impl EGLImages { + pub fn num_planes(&self) -> usize { + self.format.num_planes() + } + + pub unsafe fn bind_to_tex(&self, plane: usize, tex_id: c_uint) -> Result<()> { + if self.display.upgrade().is_some() { + ffi::gl::EGLImageTargetTexture2DOES(tex_id, *self.images.get(plane).chain_err(|| ErrorKind::PlaneIndexOutOfBounds)?); + match ffi::egl::GetError() as u32 { + ffi::gl::NO_ERROR => Ok(()), + err => bail!(ErrorKind::Unknown(err)), + } + } else { + bail!(ErrorKind::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); } + } + } + } +} + +impl> EGLContext { + /// Binds this EGL context to the given Wayland display. + /// + /// This will allow clients to utilize EGL to create hardware-accelerated + /// surfaces. The server will need to be able to handle egl-wl_buffers. + /// See the `wayland::drm` module. + /// + /// ## Errors + /// + /// This might return `WlExtensionNotSupported` if binding is not supported + /// by the EGL implementation. + /// + /// This might return `OtherEGLDisplayAlreadyBound` if called for the same + /// `Display` multiple times, as only one context may be bound at any given time. + pub fn bind_wl_display(&self, display: &Display) -> Result<()> { + if !self.wl_drm_support { + bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); + } + let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; + if res == 0 { + bail!(ErrorKind::OtherEGLDisplayAlreadyBound); + } + Ok(()) + } + + /// Unbinds this EGL context from the given Wayland display. + /// + /// This will stop clients from using previously available extensions + /// to utilize hardware-accelerated surface via EGL. + /// + /// ## Errors + /// + /// This might return `WlExtensionNotSupported` if binding is not supported + /// by the EGL implementation. + /// + /// This might return `OtherEGLDisplayAlreadyBound` if called for the same + /// `Display` multiple times, as only one context may be bound at any given time. + pub fn unbind_wl_display(&self, display: &Display) -> Result<()> { + if !self.wl_drm_support { + bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); + } + let res = unsafe { ffi::egl::UnbindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; + if res == 0 { + bail!(ErrorKind::NoEGLDisplayBound); + } + Ok(()) + } + + pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> Result { + if !self.egl_to_texture_support { + bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"])); + } + + let mut format: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } { + bail!(ErrorKind::BufferNotManaged); + } + 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.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } { + bail!(ErrorKind::BufferNotManaged); + } + + let mut height: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } { + bail!(ErrorKind::BufferNotManaged); + } + + let mut inverted: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.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( + *self.display, + ffi::egl::NO_CONTEXT, + ffi::egl::WAYLAND_BUFFER_WL, + buffer.ptr() as *mut _, + out.as_ptr(), + ) }; + if image == ffi::egl::NO_IMAGE_KHR { + bail!(ErrorKind::EGLImageCreationFailed); + } else { + image + } + }); + } + + Ok(EGLImages { + display: Rc::downgrade(&self.display), + width: width as u32, + height: height as u32, + y_inverted: inverted != 0, + format, + images, + buffer, + }) + } +} diff --git a/src/backend/udev.rs b/src/backend/udev.rs index b151dfc..7b2ab4e 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -87,7 +87,7 @@ impl< .into_iter() // Create devices .flat_map(|path| { - match unsafe { DrmDevice::new( + match DrmDevice::new( { match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { Ok(fd) => SessionFdDrmDevice(fd), @@ -97,7 +97,7 @@ impl< } } }, logger.clone() - ) } { + ) { // Call the handler, which might add it to the runloop Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) { // fstat them @@ -253,25 +253,23 @@ where info!(evlh.state().get(token).logger, "Device Added"); if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { let mut device = { - match unsafe { - DrmDevice::new( - { - let logger = evlh.state().get(token).logger.clone(); - match evlh.state().get_mut(token).session.open( - path, - fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY - | fcntl::O_NONBLOCK, - ) { - Ok(fd) => SessionFdDrmDevice(fd), - Err(err) => { - warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); - continue; - } + match DrmDevice::new( + { + let logger = evlh.state().get(token).logger.clone(); + match evlh.state().get_mut(token).session.open( + path, + fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY + | fcntl::O_NONBLOCK, + ) { + Ok(fd) => SessionFdDrmDevice(fd), + Err(err) => { + warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); + continue; } - }, - evlh.state().get(token).logger.clone(), - ) - } { + } + }, + evlh.state().get(token).logger.clone(), + ) { Ok(dev) => dev, Err(err) => { warn!( diff --git a/src/wayland/drm/mod.rs b/src/wayland/drm/mod.rs deleted file mode 100644 index b01dcf8..0000000 --- a/src/wayland/drm/mod.rs +++ /dev/null @@ -1,110 +0,0 @@ -use ::backend::graphics::egl::ffi; -use ::backend::graphics::egl::{EGLContext, NativeSurface}; -use ::backend::graphics::egl::EGLImage; -use wayland_server::protocol::wl_buffer::WlBuffer; -use wayland_server::Resource; - -/// Error that can occur when accessing an EGL buffer -#[derive(Debug)] -pub enum BufferAccessError { - /// This buffer is not managed by EGL - NotManaged, - /// Failed to create EGLImages from the buffer - FailedToCreateEGLImage, -} - -#[repr(i32)] -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) -> u32 { - match *self { - Format::RGB | Format::RGBA | Format::External => 1, - Format::Y_UV | Format::Y_XUXV => 2, - Format::Y_U_V => 3, - } - } -} - -pub struct EGLImages { - pub width: u32, - pub height: u32, - pub y_inverted: bool, - pub format: Format, - images: Vec, - buffer: WlBuffer, -} - -pub fn buffer_contents(buffer: WlBuffer, context: &EGLContext) -> Result<(Vec, attributes: Attributes), BufferAccessError> -where -{ - let mut format: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged); - } - - let mut width: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged); - } - - let mut height: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged); - } - - let mut inverted: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) == 0 } { - inverted = 1; - } - - let mut images = Vec::with_capacity(attributes.format.num_planes() as usize); - for i in 0..attributes.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( - context.display, - ffi::egl::NO_CONTEXT, - ffi::egl::WAYLAND_BUFFER_WL, - buffer.ptr() as *mut _, - out.as_ptr(), - ) }; - if image == ffi::egl::NO_IMAGE_KHR { - return Err(BufferAccessError::FailedToCreateEGLImage); - } else { - image - } - }); - } - - let result = EGLImages { - width: width as u32, - height: height as u32, - y_inverted: inverted != 0, - 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"), - }, - images, - buffer, - }; - - Ok(result) -} diff --git a/src/wayland/mod.rs b/src/wayland/mod.rs index 31a43ba..ccdb2d9 100644 --- a/src/wayland/mod.rs +++ b/src/wayland/mod.rs @@ -19,7 +19,6 @@ //! quickly encounter a panic. pub mod compositor; -//pub mod drm; pub mod output; pub mod seat; pub mod shm;