diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index 2112579..e784177 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc}; use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::EGLDisplay; +use smithay::backend::egl::display::EGLBufferReader; use smithay::{ reexports::wayland_server::protocol::wl_buffer::WlBuffer, wayland::shm::with_buffer_contents as shm_buffer_contents, @@ -13,15 +13,18 @@ use smithay::{ #[derive(Clone)] pub struct BufferUtils { #[cfg(feature = "egl")] - egl_display: Rc>>, + egl_buffer_reader: Rc>>, log: Logger, } impl BufferUtils { /// Creates a new `BufferUtils`. #[cfg(feature = "egl")] - pub fn new(egl_display: Rc>>, log: Logger) -> Self { - Self { egl_display, log } + pub fn new(egl_buffer_reader: Rc>>, log: Logger) -> Self { + Self { + egl_buffer_reader, + log, + } } /// Creates a new `BufferUtils`. @@ -34,7 +37,7 @@ impl BufferUtils { #[cfg(feature = "egl")] pub fn dimensions(&self, buffer: &WlBuffer) -> Option<(i32, i32)> { // Try to retrieve the EGL dimensions of this buffer, and, if that fails, the shm dimensions. - self.egl_display + self.egl_buffer_reader .borrow() .as_ref() .and_then(|display| display.egl_buffer_dimensions(buffer)) diff --git a/anvil/src/glium_drawer.rs b/anvil/src/glium_drawer.rs index e924810..b8e6f10 100644 --- a/anvil/src/glium_drawer.rs +++ b/anvil/src/glium_drawer.rs @@ -12,7 +12,7 @@ use glium::{ use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::EGLDisplay; +use smithay::backend::egl::display::EGLBufferReader; use smithay::{ backend::{ egl::{BufferAccessError, EGLImages, Format}, @@ -44,7 +44,7 @@ pub struct GliumDrawer { index_buffer: glium::IndexBuffer, programs: [glium::Program; shaders::FRAGMENT_COUNT], #[cfg(feature = "egl")] - egl_display: Rc>>, + egl_buffer_reader: Rc>>, log: Logger, } @@ -56,7 +56,11 @@ impl GliumDrawer { impl> + GLGraphicsBackend + 'static> GliumDrawer { #[cfg(feature = "egl")] - pub fn init(backend: T, egl_display: Rc>>, log: Logger) -> GliumDrawer { + pub fn init( + backend: T, + egl_buffer_reader: Rc>>, + log: Logger, + ) -> GliumDrawer { let display = backend.into(); // building the vertex buffer, which contains all the vertices that we will draw @@ -94,7 +98,7 @@ impl> + GLGraphicsBackend + 'static> GliumDrawer vertex_buffer, index_buffer, programs, - egl_display, + egl_buffer_reader, log, } } @@ -147,7 +151,7 @@ impl GliumDrawer { #[cfg(feature = "egl")] pub fn texture_from_buffer(&self, buffer: wl_buffer::WlBuffer) -> Result { // try to retrieve the egl contents of this buffer - let images = if let Some(display) = &self.egl_display.borrow().as_ref() { + let images = if let Some(display) = &self.egl_buffer_reader.borrow().as_ref() { display.egl_buffer_contents(buffer) } else { Err(BufferAccessError::NotManaged(buffer)) diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index dfc59a5..5fc1c81 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -15,7 +15,7 @@ use glium::Surface as GliumSurface; use slog::Logger; #[cfg(feature = "egl")] -use smithay::backend::egl::{EGLDisplay, EGLGraphicsBackend}; +use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; use smithay::{ backend::{ drm::{ @@ -66,6 +66,7 @@ use crate::glium_drawer::GliumDrawer; use crate::input_handler::AnvilInputHandler; use crate::shell::{init_shell, MyWindowMap, Roles}; use crate::AnvilState; +use smithay::backend::drm::gbm::GbmSurface; pub struct SessionFd(RawFd); impl AsRawFd for SessionFd { @@ -76,8 +77,7 @@ impl AsRawFd for SessionFd { type RenderDevice = EglDevice>, GbmDevice>>; -type RenderSurface = - EglSurface>, GbmDevice>>; +type RenderSurface = EglSurface>>; pub fn run_udev(mut display: Display, mut event_loop: EventLoop, log: Logger) -> Result<(), ()> { let name = display.add_socket_auto().unwrap().into_string().unwrap(); @@ -85,10 +85,10 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop, log ::std::env::set_var("WAYLAND_DISPLAY", name); #[cfg(feature = "egl")] - let active_egl_context = Rc::new(RefCell::new(None)); + let egl_buffer_reader = Rc::new(RefCell::new(None)); #[cfg(feature = "egl")] - let buffer_utils = BufferUtils::new(active_egl_context.clone(), log.clone()); + let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone()); #[cfg(not(feature = "egl"))] let buffer_utils = BufferUtils::new(log.clone()); @@ -127,7 +127,7 @@ pub fn run_udev(mut display: Display, mut event_loop: EventLoop, log UdevHandlerImpl { compositor_token, #[cfg(feature = "egl")] - active_egl_context, + egl_buffer_reader, session: session.clone(), backends: HashMap::new(), display: display.clone(), @@ -294,7 +294,7 @@ struct BackendData { struct UdevHandlerImpl { compositor_token: CompositorToken, #[cfg(feature = "egl")] - active_egl_context: Rc>>, + egl_buffer_reader: Rc>>, session: AutoSession, backends: HashMap>, display: Rc>, @@ -313,7 +313,7 @@ impl UdevHandlerImpl { #[cfg(feature = "egl")] pub fn scan_connectors( device: &mut RenderDevice, - egl_display: Rc>>, + egl_buffer_reader: Rc>>, logger: &::slog::Logger, ) -> HashMap> { // Get a set of all modesetting resource handles (excluding planes): @@ -343,7 +343,7 @@ impl UdevHandlerImpl { if let Entry::Vacant(entry) = backends.entry(crtc) { let renderer = GliumDrawer::init( device.create_surface(crtc).unwrap(), - egl_display.clone(), + egl_buffer_reader.clone(), logger.clone(), ); @@ -419,7 +419,7 @@ impl UdevHandler for UdevHandlerImpl #[cfg(feature = "egl")] { if path.canonicalize().ok() == self.primary_gpu { - *self.active_egl_context.borrow_mut() = + *self.egl_buffer_reader.borrow_mut() = device.bind_wl_display(&*self.display.borrow()).ok(); } } @@ -427,7 +427,7 @@ impl UdevHandler for UdevHandlerImpl #[cfg(feature = "egl")] let backends = Rc::new(RefCell::new(UdevHandlerImpl::::scan_connectors( &mut device, - self.active_egl_context.clone(), + self.egl_buffer_reader.clone(), &self.logger, ))); @@ -491,7 +491,7 @@ impl UdevHandler for UdevHandlerImpl #[cfg(feature = "egl")] let new_backends = UdevHandlerImpl::::scan_connectors( &mut (*evented).0, - self.active_egl_context.clone(), + self.egl_buffer_reader.clone(), &self.logger, ); #[cfg(not(feature = "egl"))] @@ -532,7 +532,7 @@ impl UdevHandler for UdevHandlerImpl #[cfg(feature = "egl")] { if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu { - *self.active_egl_context.borrow_mut() = None; + *self.egl_buffer_reader.borrow_mut() = None; } } diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index c29c3bd..bdf13e5 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -37,10 +37,10 @@ pub fn run_winit( let (renderer, mut input) = winit::init(log.clone()).map_err(|_| ())?; #[cfg(feature = "egl")] - let egl_display = Rc::new(RefCell::new( - if let Ok(egl_display) = renderer.bind_wl_display(&display) { + let egl_buffer_reader = Rc::new(RefCell::new( + if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display) { info!(log, "EGL hardware-acceleration enabled"); - Some(egl_display) + Some(egl_buffer_reader) } else { None }, @@ -48,12 +48,12 @@ pub fn run_winit( let (w, h) = renderer.get_framebuffer_dimensions(); #[cfg(feature = "egl")] - let drawer = GliumDrawer::init(renderer, egl_display.clone(), log.clone()); + let drawer = GliumDrawer::init(renderer, egl_buffer_reader.clone(), log.clone()); #[cfg(not(feature = "egl"))] let drawer = GliumDrawer::init(renderer, log.clone()); #[cfg(feature = "egl")] - let buffer_utils = BufferUtils::new(egl_display, log.clone()); + let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone()); #[cfg(not(feature = "egl"))] let buffer_utils = BufferUtils::new(log.clone()); diff --git a/src/backend/drm/egl/mod.rs b/src/backend/drm/egl/mod.rs index 4750d1c..3779cf5 100644 --- a/src/backend/drm/egl/mod.rs +++ b/src/backend/drm/egl/mod.rs @@ -12,20 +12,19 @@ use drm::control::{connector, crtc, encoder, framebuffer, plane, ResourceHandles use drm::SystemError as DrmError; use nix::libc::dev_t; use std::os::unix::io::{AsRawFd, RawFd}; -use std::rc::Rc; #[cfg(feature = "use_system_lib")] use wayland_server::Display; use super::{Device, DeviceHandler, Surface}; -use crate::backend::egl::context::GlAttributes; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; -use crate::backend::egl::EGLContext; use crate::backend::egl::Error as EGLError; #[cfg(feature = "use_system_lib")] -use crate::backend::egl::{EGLDisplay, EGLGraphicsBackend}; +use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; mod surface; pub use self::surface::*; +use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements}; +use crate::backend::egl::display::EGLDisplay; #[cfg(feature = "backend_session")] pub mod session; @@ -48,8 +47,10 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - dev: Rc>, + dev: EGLDisplay, logger: ::slog::Logger, + default_attributes: GlAttributes, + default_requirements: PixelFormatRequirements, } impl AsRawFd for EglDevice @@ -77,7 +78,7 @@ where where L: Into>, { - EglDevice::new_with_gl_attr( + EglDevice::new_with_defaults( dev, GlAttributes { version: None, @@ -85,17 +86,20 @@ where debug: cfg!(debug_assertions), vsync: true, }, + Default::default(), logger, ) } - /// Create a new [`EglDevice`] from an open device and given [`GlAttributes`] + /// Try to create a new [`EglDevice`] from an open device with the given attributes and + /// requirements as defaults for new surfaces. /// /// Returns an error if the file is no valid device or context /// creation was not successful. - pub fn new_with_gl_attr( + pub fn new_with_defaults( mut dev: D, - attributes: GlAttributes, + default_attributes: GlAttributes, + default_requirements: PixelFormatRequirements, logger: L, ) -> Result::Surface as Surface>::Error>> where @@ -107,10 +111,9 @@ where debug!(log, "Creating egl context from device"); Ok(EglDevice { - // Open the gbm device from the drm device and create a context based on that - dev: Rc::new( - EGLContext::new(dev, attributes, Default::default(), log.clone()).map_err(Error::EGL)?, - ), + dev: EGLDisplay::new(dev, log.clone()).map_err(Error::EGL)?, + default_attributes, + default_requirements, logger: log, }) } @@ -147,7 +150,7 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - type Surface = EglSurface; + type Surface = EglSurface<::Surface>; fn device_id(&self) -> dev_t { self.dev.borrow().device_id() @@ -166,15 +169,24 @@ where fn create_surface( &mut self, crtc: crtc::Handle, - ) -> Result, ::Error> { + ) -> Result::Error> { info!(self.logger, "Initializing EglSurface"); - let surface = self.dev.create_surface(crtc).map_err(Error::EGL)?; + let context = self + .dev + .create_context(self.default_attributes, self.default_requirements) + .map_err(Error::EGL)?; + let surface = self + .dev + .create_surface( + context.get_pixel_format(), + self.default_requirements.double_buffer, + context.get_config_id(), + crtc, + ) + .map_err(Error::EGL)?; - Ok(EglSurface { - dev: self.dev.clone(), - surface, - }) + Ok(EglSurface { context, surface }) } fn process_events(&mut self) { @@ -212,7 +224,7 @@ where D: Device + NativeDisplay + 'static, ::Surface: NativeSurface, { - fn bind_wl_display(&self, display: &Display) -> Result { + fn bind_wl_display(&self, display: &Display) -> Result { self.dev.bind_wl_display(display) } } diff --git a/src/backend/drm/egl/surface.rs b/src/backend/drm/egl/surface.rs index 3b84b2e..1e1f97c 100644 --- a/src/backend/drm/egl/surface.rs +++ b/src/backend/drm/egl/surface.rs @@ -1,11 +1,10 @@ use drm::control::{connector, crtc, Mode}; use nix::libc::c_void; -use std::rc::Rc; use super::Error; -use crate::backend::drm::{Device, Surface}; -use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; -use crate::backend::egl::{EGLContext, EGLSurface}; +use crate::backend::drm::Surface; +use crate::backend::egl::native::NativeSurface; +use crate::backend::egl::{get_proc_address, native, EGLContext, EGLSurface}; #[cfg(feature = "renderer_gl")] use crate::backend::graphics::gl::GLGraphicsBackend; #[cfg(feature = "renderer_gl")] @@ -13,24 +12,20 @@ use crate::backend::graphics::PixelFormat; use crate::backend::graphics::{CursorBackend, SwapBuffersError}; /// Egl surface for rendering -pub struct EglSurface +pub struct EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: native::NativeSurface + Surface, { - pub(super) dev: Rc>, - pub(super) surface: EGLSurface, + pub(super) context: EGLContext, + pub(super) surface: EGLSurface, } -impl Surface for EglSurface +impl Surface for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: NativeSurface + Surface, { - type Error = Error<<::Surface as Surface>::Error>; - type Connectors = <::Surface as Surface>::Connectors; + type Connectors = ::Connectors; + type Error = Error<::Error>; fn crtc(&self) -> crtc::Handle { (*self.surface).crtc() @@ -67,14 +62,12 @@ where } } -impl<'a, B, D> CursorBackend<'a> for EglSurface +impl<'a, N> CursorBackend<'a> for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface + CursorBackend<'a>, + N: NativeSurface + Surface + CursorBackend<'a>, { - type CursorFormat = >::CursorFormat; - type Error = >::Error; + type CursorFormat = >::CursorFormat; + type Error = >::Error; fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), Self::Error> { self.surface.set_cursor_position(x, y) @@ -93,18 +86,16 @@ where } #[cfg(feature = "renderer_gl")] -impl GLGraphicsBackend for EglSurface +impl GLGraphicsBackend for EglSurface where - B: Backend::Surface> + 'static, - D: Device + NativeDisplay + 'static, - ::Surface: NativeSurface, + N: native::NativeSurface + Surface, { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { self.surface.swap_buffers() } - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - self.dev.get_proc_address(symbol) + fn get_proc_address(&self, symbol: &str) -> *const c_void { + get_proc_address(symbol) } fn get_framebuffer_dimensions(&self) -> (u32, u32) { @@ -113,14 +104,14 @@ where } fn is_current(&self) -> bool { - self.dev.is_current() && self.surface.is_current() + self.context.is_current() && self.surface.is_current() } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { - self.surface.make_current() + self.context.make_current_with_surface(&self.surface) } fn get_pixel_format(&self) -> PixelFormat { - self.dev.get_pixel_format() + self.surface.get_pixel_format() } } diff --git a/src/backend/egl/context.rs b/src/backend/egl/context.rs index 5f0d409..938fece 100644 --- a/src/backend/egl/context.rs +++ b/src/backend/egl/context.rs @@ -1,76 +1,37 @@ //! EGL context related structs -use super::{ffi, native, EGLSurface, Error}; -use crate::backend::graphics::PixelFormat; -use nix::libc::{c_int, c_void}; -use slog; -use std::{ - cell::{Ref, RefCell, RefMut}, - ffi::{CStr, CString}, - marker::PhantomData, - mem::MaybeUninit, - ptr, - rc::Rc, -}; +use super::{ffi, Error}; +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::os::raw::c_int; +use std::ptr; +use std::sync::Arc; /// EGL context for rendering -pub struct EGLContext> { - native: RefCell, - pub(crate) context: Rc, - pub(crate) display: Rc, - pub(crate) config_id: ffi::egl::types::EGLConfig, - pub(crate) surface_attributes: Vec, +pub struct EGLContext { + context: ffi::egl::types::EGLContext, + display: Arc, + config_id: ffi::egl::types::EGLConfig, pixel_format: PixelFormat, - pub(crate) wl_drm_support: bool, - logger: slog::Logger, - _backend: PhantomData, } -impl> EGLContext { +impl EGLContext { /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay) - pub fn new( - native: N, - attributes: GlAttributes, - reqs: PixelFormatRequirements, - logger: L, - ) -> Result, Error> - where - L: Into>, - { - let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl")); - let ptr = native.ptr()?; - let (context, display, config_id, surface_attributes, pixel_format, wl_drm_support) = - unsafe { EGLContext::::new_internal(ptr, attributes, reqs, log.clone()) }?; - - Ok(EGLContext { - native: RefCell::new(native), - context, - display, - config_id, - surface_attributes, - pixel_format, - wl_drm_support, - logger: log, - _backend: PhantomData, - }) - } - - unsafe fn new_internal( - ptr: ffi::NativeDisplayType, + pub(crate) fn new( + display: &EGLDisplay, mut attributes: GlAttributes, reqs: PixelFormatRequirements, - log: ::slog::Logger, - ) -> Result< - ( - Rc, - Rc, - ffi::egl::types::EGLConfig, - Vec, - PixelFormat, - bool, - ), - Error, - > { + log: L, + ) -> Result + where + L: Into>, + B: native::Backend, + N: native::NativeDisplay, + { + let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl")); + // If no version is given, try OpenGLES 3.0, if available, // fallback to 2.0 otherwise let version = match attributes.version { @@ -79,13 +40,13 @@ impl> EGLContext { None => { debug!(log, "Trying to initialize EGL with OpenGLES 3.0"); attributes.version = Some((3, 0)); - match EGLContext::::new_internal(ptr, attributes, reqs, log.clone()) { + match EGLContext::new(display, attributes, reqs, log.clone()) { Ok(x) => return Ok(x), Err(err) => { warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err); debug!(log, "Trying to initialize EGL with OpenGLES 2.0"); attributes.version = Some((2, 0)); - return EGLContext::::new_internal(ptr, attributes, reqs, log); + return EGLContext::new(display, attributes, reqs, log.clone()); } } } @@ -102,265 +63,11 @@ impl> EGLContext { } }; - ffi::egl::LOAD.call_once(|| { - fn constrain(f: F) -> F - where - F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, - { - f - }; - - ffi::egl::load_with(|sym| { - let name = CString::new(sym).unwrap(); - let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes()); - match symbol { - Ok(x) => *x as *const _, - Err(_) => ptr::null(), - } - }); - let proc_address = constrain(|sym| { - let addr = CString::new(sym).unwrap(); - let addr = addr.as_ptr(); - ffi::egl::GetProcAddress(addr) as *const _ - }); - ffi::egl::load_with(&proc_address); - ffi::egl::BindWaylandDisplayWL::load_with(&proc_address); - ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address); - ffi::egl::QueryWaylandBufferWL::load_with(&proc_address); - }); - - // the first step is to query the list of extensions without any display, if supported - let dp_extensions = { - let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32); - - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error - if p.is_null() { - vec![] - } else { - let p = CStr::from_ptr(p); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } - }; - - debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions); - - let display = B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone()); - if display == ffi::egl::NO_DISPLAY { - error!(log, "EGL Display is not valid"); - return Err(Error::DisplayNotSupported); - } - - let egl_version = { - let mut major: MaybeUninit = MaybeUninit::uninit(); - let mut minor: MaybeUninit = MaybeUninit::uninit(); - - if ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) == 0 { - return Err(Error::InitFailed); - } - let major = major.assume_init(); - let minor = minor.assume_init(); - - info!(log, "EGL Initialized"); - info!(log, "EGL Version: {:?}", (major, minor)); - - (major, minor) - }; - - // 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 = CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32)); - let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); - list.split(' ').map(|e| e.to_string()).collect::>() - } else { - vec![] - }; - - info!(log, "EGL Extensions: {:?}", extensions); - - if egl_version >= (1, 2) && ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 { - error!(log, "OpenGLES not supported by the underlying EGL implementation"); - return Err(Error::OpenGlesNotSupported); - } - - let descriptor = { - let mut out: Vec = Vec::with_capacity(37); - - if egl_version >= (1, 2) { - trace!(log, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER"); - out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int); - out.push(ffi::egl::RGB_BUFFER as c_int); - } - - trace!(log, "Setting SURFACE_TYPE to WINDOW"); - - out.push(ffi::egl::SURFACE_TYPE as c_int); - // TODO: Some versions of Mesa report a BAD_ATTRIBUTE error - // if we ask for PBUFFER_BIT as well as WINDOW_BIT - out.push((ffi::egl::WINDOW_BIT) as c_int); - - match version { - (3, _) => { - if egl_version < (1, 3) { - error!( - log, - "OpenglES 3.* is not supported on EGL Versions lower then 1.3" - ); - return Err(Error::NoAvailablePixelFormat); - } - trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3"); - out.push(ffi::egl::RENDERABLE_TYPE as c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as c_int); - trace!(log, "Setting CONFORMANT to OPENGL_ES3"); - out.push(ffi::egl::CONFORMANT as c_int); - out.push(ffi::egl::OPENGL_ES3_BIT as c_int); - } - (2, _) => { - if egl_version < (1, 3) { - error!( - log, - "OpenglES 2.* is not supported on EGL Versions lower then 1.3" - ); - return Err(Error::NoAvailablePixelFormat); - } - trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2"); - out.push(ffi::egl::RENDERABLE_TYPE as c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as c_int); - trace!(log, "Setting CONFORMANT to OPENGL_ES2"); - out.push(ffi::egl::CONFORMANT as c_int); - out.push(ffi::egl::OPENGL_ES2_BIT as c_int); - } - (_, _) => unreachable!(), - }; - - if let Some(hardware_accelerated) = reqs.hardware_accelerated { - out.push(ffi::egl::CONFIG_CAVEAT as c_int); - out.push(if hardware_accelerated { - trace!(log, "Setting CONFIG_CAVEAT to NONE"); - ffi::egl::NONE as c_int - } else { - trace!(log, "Setting CONFIG_CAVEAT to SLOW_CONFIG"); - ffi::egl::SLOW_CONFIG as c_int - }); - } - - if let Some(color) = reqs.color_bits { - trace!(log, "Setting RED_SIZE to {}", color / 3); - out.push(ffi::egl::RED_SIZE as c_int); - out.push((color / 3) as c_int); - trace!( - log, - "Setting GREEN_SIZE to {}", - color / 3 + if color % 3 != 0 { 1 } else { 0 } - ); - out.push(ffi::egl::GREEN_SIZE as c_int); - out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int); - trace!( - log, - "Setting BLUE_SIZE to {}", - color / 3 + if color % 3 == 2 { 1 } else { 0 } - ); - out.push(ffi::egl::BLUE_SIZE as c_int); - out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int); - } - - if let Some(alpha) = reqs.alpha_bits { - trace!(log, "Setting ALPHA_SIZE to {}", alpha); - out.push(ffi::egl::ALPHA_SIZE as c_int); - out.push(alpha as c_int); - } - - if let Some(depth) = reqs.depth_bits { - trace!(log, "Setting DEPTH_SIZE to {}", depth); - out.push(ffi::egl::DEPTH_SIZE as c_int); - out.push(depth as c_int); - } - - if let Some(stencil) = reqs.stencil_bits { - trace!(log, "Setting STENCIL_SIZE to {}", stencil); - out.push(ffi::egl::STENCIL_SIZE as c_int); - out.push(stencil as c_int); - } - - if let Some(multisampling) = reqs.multisampling { - trace!(log, "Setting SAMPLES to {}", multisampling); - out.push(ffi::egl::SAMPLES as c_int); - out.push(multisampling as c_int); - } - - if reqs.stereoscopy { - error!(log, "Stereoscopy is currently unsupported (sorry!)"); - return Err(Error::NoAvailablePixelFormat); - } - - out.push(ffi::egl::NONE as c_int); - out - }; - - // calling `eglChooseConfig` - let mut config_id = MaybeUninit::uninit(); - let mut num_configs = MaybeUninit::uninit(); - if ffi::egl::ChooseConfig( - display, - descriptor.as_ptr(), - config_id.as_mut_ptr(), - 1, - num_configs.as_mut_ptr(), - ) == 0 - { - return Err(Error::ConfigFailed); - } - - let config_id = config_id.assume_init(); - let num_configs = num_configs.assume_init(); - - if num_configs == 0 { - error!(log, "No matching color format found"); - return Err(Error::NoAvailablePixelFormat); - } - - // analyzing each config - macro_rules! attrib { - ($display:expr, $config:expr, $attr:expr) => {{ - let mut value = MaybeUninit::uninit(); - let res = ffi::egl::GetConfigAttrib( - $display, - $config, - $attr as ffi::egl::types::EGLint, - value.as_mut_ptr(), - ); - if res == 0 { - return Err(Error::ConfigFailed); - } - value.assume_init() - }}; - }; - - let desc = PixelFormat { - hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT) - != ffi::egl::SLOW_CONFIG as i32, - color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8 - + attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8 - + attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8, - alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8, - depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8, - stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8, - stereoscopy: false, - double_buffer: true, - multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) { - 0 | 1 => None, - a => Some(a as u16), - }, - srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that - }; - - info!(log, "Selected color format: {:?}", desc); + let (pixel_format, config_id) = display.choose_config(attributes, reqs)?; let mut context_attributes = Vec::with_capacity(10); - if egl_version >= (1, 5) || extensions.iter().any(|s| *s == "EGL_KHR_create_context") { + if display.egl_version >= (1, 5) || display.extensions.iter().any(|s| s == "EGL_KHR_create_context") { trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0); context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32); context_attributes.push(version.0 as i32); @@ -368,7 +75,7 @@ impl> EGLContext { context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32); context_attributes.push(version.1 as i32); - if attributes.debug && egl_version >= (1, 5) { + if attributes.debug && display.egl_version >= (1, 5) { trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE"); context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32); context_attributes.push(ffi::egl::TRUE as i32); @@ -376,7 +83,7 @@ impl> EGLContext { context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32); context_attributes.push(0); - } else if egl_version >= (1, 3) { + } else if display.egl_version >= (1, 3) { trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0); context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32); context_attributes.push(version.0 as i32); @@ -385,116 +92,105 @@ impl> EGLContext { context_attributes.push(ffi::egl::NONE as i32); trace!(log, "Creating EGL context..."); - let context = ffi::egl::CreateContext(display, config_id, ptr::null(), context_attributes.as_ptr()); + // TODO: Support shared contexts + let context = unsafe { + ffi::egl::CreateContext( + **display.display, + config_id, + ptr::null(), + context_attributes.as_ptr(), + ) + }; if context.is_null() { - match ffi::egl::GetError() as u32 { + match unsafe { ffi::egl::GetError() } as u32 { ffi::egl::BAD_ATTRIBUTE => return Err(Error::CreationFailed), err_no => return Err(Error::Unknown(err_no)), } } - debug!(log, "EGL context successfully created"); - - let surface_attributes = { - let mut out: Vec = Vec::with_capacity(3); - - match reqs.double_buffer { - Some(true) => { - trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER"); - out.push(ffi::egl::RENDER_BUFFER as c_int); - out.push(ffi::egl::BACK_BUFFER as c_int); - } - Some(false) => { - trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER"); - out.push(ffi::egl::RENDER_BUFFER as c_int); - out.push(ffi::egl::SINGLE_BUFFER as c_int); - } - None => {} - } - - out.push(ffi::egl::NONE as i32); - out - }; info!(log, "EGL context created"); - // make current and get list of gl extensions - ffi::egl::MakeCurrent(display as *const _, ptr::null(), ptr::null(), context as *const _); - - Ok(( - Rc::new(context as *const _), - Rc::new(display as *const _), + Ok(EGLContext { + context, + display: display.display.clone(), config_id, - surface_attributes, - desc, - extensions.iter().any(|s| *s == "EGL_WL_bind_wayland_display"), - )) - } - - /// Creates a surface for rendering - pub fn create_surface(&self, args: N::Arguments) -> Result, 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 - })?; - EGLSurface::new(self, surface).map(|x| { - debug!(self.logger, "EGL surface successfully created"); - x + pixel_format, }) } - /// Returns the address of an OpenGL function. + /// Makes the OpenGL context the current context in the current thread with a surface to + /// read/write to. /// /// # Safety /// - /// The context must have been made current before this function is called. - pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - let addr = CString::new(symbol.as_bytes()).unwrap(); - let addr = addr.as_ptr(); - ffi::egl::GetProcAddress(addr) as *const _ + /// This function is marked unsafe, because the context cannot be made current + /// on multiple threads. + pub unsafe fn make_current_with_surface( + &self, + surface: &EGLSurface, + ) -> ::std::result::Result<(), SwapBuffersError> + 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(()) + } + } + + /// Makes the OpenGL context the current context in the current thread with no surface bound. + /// + /// # 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(()) + } } /// 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 + pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { + self.config_id } /// Returns the pixel format of the main framebuffer of the context. pub fn get_pixel_format(&self) -> PixelFormat { self.pixel_format } - - /// Borrow the underlying native display. - /// - /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). - /// Multiple read-only borrows are possible. Borrowing the - /// backend while there is a mutable reference will panic. - pub fn borrow(&self) -> Ref<'_, N> { - self.native.borrow() - } - - /// Borrow the underlying native display mutably. - /// - /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). - /// Holding any other borrow while trying to borrow the backend - /// mutably will panic. Note that EGL will borrow the display - /// mutably during surface creation. - pub fn borrow_mut(&self) -> RefMut<'_, N> { - self.native.borrow_mut() - } } -unsafe impl + Send> Send for EGLContext {} -unsafe impl + Sync> Sync for EGLContext {} - -impl> Drop for 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 - ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _); - ffi::egl::Terminate((*self.display) 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); } } } @@ -571,3 +267,70 @@ impl Default for PixelFormatRequirements { } } } + +impl PixelFormatRequirements { + /// Append the requirements to the given attribute list + pub fn create_attributes(&self, out: &mut Vec, logger: &slog::Logger) -> Result<(), Error> { + if let Some(hardware_accelerated) = self.hardware_accelerated { + out.push(ffi::egl::CONFIG_CAVEAT as c_int); + out.push(if hardware_accelerated { + trace!(logger, "Setting CONFIG_CAVEAT to NONE"); + ffi::egl::NONE as c_int + } else { + trace!(logger, "Setting CONFIG_CAVEAT to SLOW_CONFIG"); + ffi::egl::SLOW_CONFIG as c_int + }); + } + + if let Some(color) = self.color_bits { + trace!(logger, "Setting RED_SIZE to {}", color / 3); + out.push(ffi::egl::RED_SIZE as c_int); + out.push((color / 3) as c_int); + trace!( + logger, + "Setting GREEN_SIZE to {}", + color / 3 + if color % 3 != 0 { 1 } else { 0 } + ); + out.push(ffi::egl::GREEN_SIZE as c_int); + out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int); + trace!( + logger, + "Setting BLUE_SIZE to {}", + color / 3 + if color % 3 == 2 { 1 } else { 0 } + ); + out.push(ffi::egl::BLUE_SIZE as c_int); + out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int); + } + + if let Some(alpha) = self.alpha_bits { + trace!(logger, "Setting ALPHA_SIZE to {}", alpha); + out.push(ffi::egl::ALPHA_SIZE as c_int); + out.push(alpha as c_int); + } + + if let Some(depth) = self.depth_bits { + trace!(logger, "Setting DEPTH_SIZE to {}", depth); + out.push(ffi::egl::DEPTH_SIZE as c_int); + out.push(depth as c_int); + } + + if let Some(stencil) = self.stencil_bits { + trace!(logger, "Setting STENCIL_SIZE to {}", stencil); + out.push(ffi::egl::STENCIL_SIZE as c_int); + out.push(stencil as c_int); + } + + if let Some(multisampling) = self.multisampling { + trace!(logger, "Setting SAMPLES to {}", multisampling); + out.push(ffi::egl::SAMPLES as c_int); + out.push(multisampling as c_int); + } + + if self.stereoscopy { + error!(logger, "Stereoscopy is currently unsupported (sorry!)"); + return Err(Error::NoAvailablePixelFormat); + } + + Ok(()) + } +} diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs new file mode 100644 index 0000000..20f9143 --- /dev/null +++ b/src/backend/egl/display.rs @@ -0,0 +1,620 @@ +//! Type safe native types for safe egl initialisation + +#[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, +}; +use std::sync::Arc; + +use std::ptr; + +use nix::libc::{c_int, c_void}; + +#[cfg(feature = "wayland_frontend")] +use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; +#[cfg(feature = "use_system_lib")] +use wayland_sys::server::wl_display; + +use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements}; +#[cfg(feature = "renderer_gl")] +use crate::backend::graphics::gl::ffi as gl_ffi; +use crate::backend::graphics::PixelFormat; +use std::cell::{Ref, RefCell, RefMut}; +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> { + native: RefCell, + pub(crate) display: Arc, + pub(crate) egl_version: (i32, i32), + pub(crate) extensions: Vec, + logger: slog::Logger, + _backend: PhantomData, +} + +impl> EGLDisplay { + /// Create a new [`EGLDisplay`] from a given [`NativeDisplay`](native::NativeDisplay) + pub fn new(native: N, logger: L) -> Result, Error> + where + L: Into>, + { + let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl")); + let ptr = native.ptr()?; + + ffi::egl::LOAD.call_once(|| unsafe { + fn constrain(f: F) -> F + where + F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, + { + f + }; + + ffi::egl::load_with(|sym| { + let name = CString::new(sym).unwrap(); + let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes()); + match symbol { + Ok(x) => *x as *const _, + Err(_) => ptr::null(), + } + }); + let proc_address = constrain(|sym| get_proc_address(sym)); + ffi::egl::load_with(&proc_address); + ffi::egl::BindWaylandDisplayWL::load_with(&proc_address); + ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address); + ffi::egl::QueryWaylandBufferWL::load_with(&proc_address); + }); + + // 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); + + // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise + // `eglQueryString` returns an error + if p.is_null() { + vec![] + } else { + let p = CStr::from_ptr(p); + let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } + }; + + 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 egl_version = { + let mut major: MaybeUninit = MaybeUninit::uninit(); + let mut minor: MaybeUninit = MaybeUninit::uninit(); + + if unsafe { ffi::egl::Initialize(display, major.as_mut_ptr(), minor.as_mut_ptr()) } == 0 { + return Err(Error::InitFailed); + } + let major = unsafe { major.assume_init() }; + let minor = unsafe { minor.assume_init() }; + + info!(log, "EGL Initialized"); + info!(log, "EGL Version: {:?}", (major, minor)); + + (major, minor) + }; + + // 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 list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new()); + list.split(' ').map(|e| e.to_string()).collect::>() + } 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); + } + + Ok(EGLDisplay { + native: RefCell::new(native), + display: Arc::new(EGLDisplayHandle { handle: display }), + egl_version, + extensions, + logger: log, + _backend: PhantomData, + }) + } + + /// Finds a compatible [`EGLConfig`] for a given set of requirements + pub fn choose_config( + &self, + attributes: GlAttributes, + reqs: PixelFormatRequirements, + ) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> { + let descriptor = { + let mut out: Vec = Vec::with_capacity(37); + + if self.egl_version >= (1, 2) { + trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER"); + out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int); + out.push(ffi::egl::RGB_BUFFER as c_int); + } + + trace!(self.logger, "Setting SURFACE_TYPE to WINDOW"); + + out.push(ffi::egl::SURFACE_TYPE as c_int); + // TODO: Some versions of Mesa report a BAD_ATTRIBUTE error + // if we ask for PBUFFER_BIT as well as WINDOW_BIT + out.push((ffi::egl::WINDOW_BIT) as c_int); + + match attributes.version { + Some((3, _)) => { + if self.egl_version < (1, 3) { + error!( + self.logger, + "OpenglES 3.* is not supported on EGL Versions lower then 1.3" + ); + return Err(Error::NoAvailablePixelFormat); + } + trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES3"); + out.push(ffi::egl::RENDERABLE_TYPE as c_int); + out.push(ffi::egl::OPENGL_ES3_BIT as c_int); + trace!(self.logger, "Setting CONFORMANT to OPENGL_ES3"); + out.push(ffi::egl::CONFORMANT as c_int); + out.push(ffi::egl::OPENGL_ES3_BIT as c_int); + } + Some((2, _)) => { + if self.egl_version < (1, 3) { + error!( + self.logger, + "OpenglES 2.* is not supported on EGL Versions lower then 1.3" + ); + return Err(Error::NoAvailablePixelFormat); + } + trace!(self.logger, "Setting RENDERABLE_TYPE to OPENGL_ES2"); + out.push(ffi::egl::RENDERABLE_TYPE as c_int); + out.push(ffi::egl::OPENGL_ES2_BIT as c_int); + trace!(self.logger, "Setting CONFORMANT to OPENGL_ES2"); + out.push(ffi::egl::CONFORMANT as c_int); + out.push(ffi::egl::OPENGL_ES2_BIT as c_int); + } + Some(ver) => { + return Err(Error::OpenGlVersionNotSupported(ver)); + } + None => { + return Err(Error::OpenGlVersionNotSupported((0, 0))); + } + }; + + reqs.create_attributes(&mut out, &self.logger)?; + + out.push(ffi::egl::NONE as c_int); + out + }; + + // calling `eglChooseConfig` + let mut num_configs = unsafe { std::mem::zeroed() }; + if unsafe { + ffi::egl::ChooseConfig( + **self.display, + descriptor.as_ptr(), + std::ptr::null_mut(), + 0, + &mut num_configs, + ) + } == 0 + { + return 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 { + ffi::egl::ChooseConfig( + **self.display, + descriptor.as_ptr(), + config_ids.as_mut_ptr(), + num_configs, + &mut num_configs, + ) + } == 0 + { + return Err(Error::ConfigFailed); + } + + // 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 { + let mut min_swap_interval = 0; + 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; + } + + let mut max_swap_interval = 0; + 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; + } + + true + }) + .collect::>(); + + if config_ids.is_empty() { + return Err(Error::NoAvailablePixelFormat); + } + + // TODO: Improve config selection + let config_id = config_ids[0]; + + // analyzing each config + macro_rules! attrib { + ($display:expr, $config:expr, $attr:expr) => {{ + let mut value = MaybeUninit::uninit(); + let res = ffi::egl::GetConfigAttrib( + **$display, + $config, + $attr as ffi::egl::types::EGLint, + value.as_mut_ptr(), + ); + if res == 0 { + return Err(Error::ConfigFailed); + } + value.assume_init() + }}; + }; + + let desc = unsafe { + PixelFormat { + hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT) + != ffi::egl::SLOW_CONFIG as i32, + color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8 + + attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8 + + attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8, + alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8, + depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8, + stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8, + stereoscopy: false, + multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) { + 0 | 1 => None, + a => Some(a as u16), + }, + srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that + } + }; + + info!(self.logger, "Selected color format: {:?}", desc); + + Ok((desc, config_id)) + } + + /// Create a new [`EGLContext`](::backend::egl::EGLContext) + pub fn create_context( + &self, + attributes: GlAttributes, + reqs: PixelFormatRequirements, + ) -> Result { + EGLContext::new(&self, attributes, reqs, self.logger.clone()) + } + + /// Creates a surface for rendering + pub fn create_surface( + &self, + pixel_format: PixelFormat, + double_buffer: Option, + config: ffi::egl::types::EGLConfig, + args: N::Arguments, + ) -> Result, 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 + })?; + + EGLSurface::new( + self.display.clone(), + pixel_format, + double_buffer, + config, + surface, + self.logger.clone(), + ) + .map(|x| { + debug!(self.logger, "EGL surface successfully created"); + x + }) + } + + /// Returns the runtime egl version of this display + pub fn get_egl_version(&self) -> (i32, i32) { + self.egl_version + } + + /// Returns the supported extensions of this display + pub fn get_extensions(&self) -> Vec { + self.extensions.clone() + } + + /// Borrow the underlying native display. + /// + /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). + /// Multiple read-only borrows are possible. Borrowing the + /// backend while there is a mutable reference will panic. + pub fn borrow(&self) -> Ref<'_, N> { + self.native.borrow() + } + + /// Borrow the underlying native display mutably. + /// + /// This follows the same semantics as [`std::cell:RefCell`](std::cell::RefCell). + /// Holding any other borrow while trying to borrow the backend + /// mutably will panic. Note that EGL will borrow the display + /// mutably during surface creation. + pub fn borrow_mut(&self) -> RefMut<'_, N> { + self.native.borrow_mut() + } +} + +#[cfg(feature = "use_system_lib")] +impl> EGLGraphicsBackend for EGLDisplay { + /// Binds this EGL display 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-[`WlBuffer`]s. + /// + /// ## Errors + /// + /// This might return [`EglExtensionNotSupported`](ErrorKind::EglExtensionNotSupported) + /// if binding is not supported by the EGL implementation. + /// + /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::OtherEGLDisplayAlreadyBound) + /// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time. + fn bind_wl_display(&self, display: &Display) -> Result { + 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); + } + Ok(EGLBufferReader::new( + self.display.clone(), + display.c_ptr(), + &self.extensions, + )) + } +} + +/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s. +/// +/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`]. +#[cfg(feature = "use_system_lib")] +pub struct EGLBufferReader { + display: Arc, + wayland: *mut wl_display, + #[cfg(feature = "renderer_gl")] + gl: gl_ffi::Gles2, + #[cfg(feature = "renderer_gl")] + egl_to_texture_support: bool, +} + +#[cfg(feature = "use_system_lib")] +impl EGLBufferReader { + fn new(display: Arc, 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 _); + + Self { + display, + wayland, + #[cfg(feature = "renderer_gl")] + egl_to_texture_support: extensions + .iter() + .any(|s| s == "GL_OES_EGL_image" || s == "GL_OES_EGL_image_base"), + #[cfg(feature = "renderer_gl")] + gl, + } + } + + /// Try to receive [`EGLImages`] from a given [`WlBuffer`]. + /// + /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) + /// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer + /// to render it another way. + pub fn egl_buffer_contents( + &self, + buffer: WlBuffer, + ) -> ::std::result::Result { + 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`]. + /// + /// 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)> { + 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 !self.wayland.is_null() { + unsafe { + ffi::egl::UnbindWaylandDisplayWL(**self.display, self.wayland as _); + } + } + } +} diff --git a/src/backend/egl/mod.rs b/src/backend/egl/mod.rs index 6909538..dce4a85 100644 --- a/src/backend/egl/mod.rs +++ b/src/backend/egl/mod.rs @@ -21,28 +21,30 @@ #[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 std::fmt; #[cfg(feature = "wayland_frontend")] use wayland_server::{protocol::wl_buffer::WlBuffer, Display}; -#[cfg(feature = "use_system_lib")] -use wayland_sys::server::wl_display; pub mod context; pub use self::context::EGLContext; mod error; pub use self::error::Error; +use nix::libc::c_void; + #[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)] pub mod ffi; use self::ffi::egl::types::EGLImage; +pub mod display; pub mod native; 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::Arc; /// Error that can happen on optional EGL features #[derive(Debug, Clone, PartialEq)] @@ -61,6 +63,17 @@ impl fmt::Display for EglExtensionNotSupportedError { impl ::std::error::Error for EglExtensionNotSupportedError {} +/// Returns the address of an OpenGL function. +/// +/// Result is independent of displays and does not guarantee an extension is actually supported at runtime. +pub fn get_proc_address(symbol: &str) -> *const c_void { + unsafe { + let addr = CString::new(symbol.as_bytes()).unwrap(); + let addr = addr.as_ptr(); + ffi::egl::GetProcAddress(addr) as *const _ + } +} + /// Error that can occur when accessing an EGL buffer #[cfg(feature = "wayland_frontend")] #[derive(thiserror::Error)] @@ -155,7 +168,7 @@ impl Format { /// Images of the EGL-based [`WlBuffer`]. #[cfg(feature = "wayland_frontend")] pub struct EGLImages { - display: Weak, + display: Arc, /// Width in pixels pub width: u32, /// Height in pixels @@ -192,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(); @@ -234,7 +241,7 @@ impl Drop for EGLImages { } /// Trait any backend type may implement that allows binding a [`Display`](wayland_server::Display) -/// to create an [`EGLDisplay`] for EGL-based [`WlBuffer`]s. +/// to create an [`EGLBufferReader`](display::EGLBufferReader) for EGL-based [`WlBuffer`]s. #[cfg(feature = "use_system_lib")] pub trait EGLGraphicsBackend { /// Binds this EGL context to the given Wayland display. @@ -249,226 +256,5 @@ pub trait EGLGraphicsBackend { /// /// This might return [`OtherEGLDisplayAlreadyBound`](ErrorKind::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) -> Result; -} - -/// Type to receive [`EGLImages`] for EGL-based [`WlBuffer`]s. -/// -/// Can be created by using [`EGLGraphicsBackend::bind_wl_display`]. -#[cfg(feature = "use_system_lib")] -pub struct EGLDisplay { - egl: Weak, - wayland: *mut wl_display, - #[cfg(feature = "renderer_gl")] - gl: gl_ffi::Gles2, - #[cfg(feature = "renderer_gl")] - egl_to_texture_support: bool, -} - -#[cfg(feature = "use_system_lib")] -impl EGLDisplay { - fn new>( - context: &EGLContext, - 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: { - // the list of gl extensions supported by the context - 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, - } - } - - /// Try to receive [`EGLImages`] from a given [`WlBuffer`]. - /// - /// In case the buffer is not managed by EGL (but e.g. the [`wayland::shm` module](::wayland::shm)) - /// a [`BufferAccessError::NotManaged`](::backend::egl::BufferAccessError::NotManaged) is returned with the original buffer - /// to render it another way. - pub fn egl_buffer_contents( - &self, - buffer: WlBuffer, - ) -> ::std::result::Result { - if let Some(display) = self.egl.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: 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) - } - } - - /// Try to receive the dimensions of a given [`WlBuffer`]. - /// - /// 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.egl.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 - } - } -} - -#[cfg(feature = "use_system_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 = "use_system_lib")] -impl EGLGraphicsBackend for Rc { - fn bind_wl_display(&self, display: &Display) -> Result { - (**self).bind_wl_display(display) - } -} - -#[cfg(feature = "use_system_lib")] -impl> EGLGraphicsBackend for EGLContext { - fn bind_wl_display(&self, display: &Display) -> Result { - if !self.wl_drm_support { - 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); - } - Ok(EGLDisplay::new(self, display.c_ptr())) - } + fn bind_wl_display(&self, display: &Display) -> Result; } diff --git a/src/backend/egl/surface.rs b/src/backend/egl/surface.rs index c3687d0..1efe67c 100644 --- a/src/backend/egl/surface.rs +++ b/src/backend/egl/surface.rs @@ -1,21 +1,22 @@ //! EGL surface related structs -use super::{ffi, native, EGLContext, Error}; -use crate::backend::graphics::SwapBuffersError; +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; use std::{ cell::Cell, ops::{Deref, DerefMut}, - rc::{Rc, Weak}, }; /// EGL surface of a given EGL context for rendering pub struct EGLSurface { - context: Weak, - display: Weak, + display: Arc, native: N, - surface: Cell, + pub(crate) surface: Cell, config_id: ffi::egl::types::EGLConfig, + pixel_format: PixelFormat, surface_attributes: Vec, } @@ -33,17 +34,42 @@ impl DerefMut for EGLSurface { } impl EGLSurface { - pub(crate) fn new, D: native::NativeDisplay>( - context: &EGLContext, + pub(crate) fn new( + display: Arc, + pixel_format: PixelFormat, + double_buffered: Option, + config: ffi::egl::types::EGLConfig, native: N, - ) -> Result, Error> { + log: L, + ) -> Result, Error> + where + L: Into>, + { + let log = crate::slog_or_stdlog(log.into()).new(o!("smithay_module" => "renderer_egl")); + + let surface_attributes = { + let mut out: Vec = Vec::with_capacity(3); + + match double_buffered { + Some(true) => { + trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER"); + out.push(ffi::egl::RENDER_BUFFER as c_int); + out.push(ffi::egl::BACK_BUFFER as c_int); + } + Some(false) => { + trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER"); + out.push(ffi::egl::RENDER_BUFFER as c_int); + out.push(ffi::egl::SINGLE_BUFFER as c_int); + } + None => {} + } + + out.push(ffi::egl::NONE as i32); + out + }; + let surface = unsafe { - ffi::egl::CreateWindowSurface( - *context.display, - context.config_id, - native.ptr(), - context.surface_attributes.as_ptr(), - ) + ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr()) }; if surface.is_null() { @@ -51,12 +77,12 @@ impl EGLSurface { } Ok(EGLSurface { - context: Rc::downgrade(&context.context), - display: Rc::downgrade(&context.display), + display, native, surface: Cell::new(surface), - config_id: context.config_id, - surface_attributes: context.surface_attributes.clone(), + config_id: config, + pixel_format, + surface_attributes, }) } @@ -65,86 +91,56 @@ impl EGLSurface { 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(()) } - /// Makes the OpenGL context the current context in the current thread. - /// - /// # 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> { - if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) { - let ret = ffi::egl::MakeCurrent( - (*display) as *const _, - self.surface.get() as *const _, - self.surface.get() as *const _, - (*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(()) - } - } else { - Err(SwapBuffersError::ContextLost) + /// Returns true if the OpenGL surface is the current one in the thread. + pub fn is_current(&self) -> bool { + unsafe { + ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ + && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ } } - /// Returns true if the OpenGL surface is the current one in the thread. - pub fn is_current(&self) -> bool { - if self.context.upgrade().is_some() { - unsafe { - ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ - && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ - } - } else { - false - } + /// Returns the egl config for this context + pub fn get_config_id(&self) -> ffi::egl::types::EGLConfig { + self.config_id + } + + /// Returns the pixel format of the main framebuffer of the context. + pub fn get_pixel_format(&self) -> PixelFormat { + self.pixel_format } } impl Drop for EGLSurface { 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 _); } } } diff --git a/src/backend/graphics/format.rs b/src/backend/graphics/format.rs index 96bef69..5262cba 100644 --- a/src/backend/graphics/format.rs +++ b/src/backend/graphics/format.rs @@ -13,8 +13,6 @@ pub struct PixelFormat { pub stencil_bits: u8, /// is stereoscopy enabled pub stereoscopy: bool, - /// is double buffering enabled - pub double_buffer: bool, /// number of samples used for multisampling if enabled pub multisampling: Option, /// is srgb enabled diff --git a/src/backend/graphics/gl.rs b/src/backend/graphics/gl.rs index d0d04a0..272a23b 100644 --- a/src/backend/graphics/gl.rs +++ b/src/backend/graphics/gl.rs @@ -18,11 +18,7 @@ pub trait GLGraphicsBackend { fn swap_buffers(&self) -> Result<(), SwapBuffersError>; /// Returns the address of an OpenGL function. - /// - /// # Safety - /// - /// The context must have been made current before this function is called. - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void; + fn get_proc_address(&self, symbol: &str) -> *const c_void; /// Returns the dimensions of the window, or screen, etc in points. /// @@ -51,5 +47,5 @@ pub trait GLGraphicsBackend { /// and may only be used in combination with the backend. Using this with any /// other gl context or after the backend was dropped *may* cause undefined behavior. pub fn load_raw_gl(backend: &B) -> Gles2 { - Gles2::load_with(|s| unsafe { backend.get_proc_address(s) as *const _ }) + Gles2::load_with(|s| backend.get_proc_address(s) as *const _) } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index f821730..4c4e6ea 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,10 +1,9 @@ //! Implementation of backend traits for types provided by `winit` +use crate::backend::egl::display::EGLDisplay; +use crate::backend::egl::get_proc_address; use crate::backend::{ - egl::{ - context::GlAttributes, native, EGLContext, EGLDisplay, EGLGraphicsBackend, EGLSurface, - Error as EGLError, - }, + egl::{context::GlAttributes, native, EGLContext, EGLSurface, Error as EGLError}, graphics::{gl::GLGraphicsBackend, CursorBackend, PixelFormat, SwapBuffersError}, input::{ Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, @@ -33,6 +32,9 @@ use winit::{ window::{CursorIcon, Window as WinitWindow, WindowBuilder}, }; +#[cfg(feature = "use_system_lib")] +use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; + /// Errors thrown by the `winit` backends #[derive(thiserror::Error, Debug)] pub enum Error { @@ -49,11 +51,13 @@ pub enum Error { enum Window { Wayland { - context: EGLContext, + display: EGLDisplay, + context: EGLContext, surface: EGLSurface, }, X11 { - context: EGLContext, + display: EGLDisplay, + context: EGLContext, surface: EGLSurface, }, } @@ -61,8 +65,8 @@ enum Window { impl Window { fn window(&self) -> Ref<'_, WinitWindow> { match *self { - Window::Wayland { ref context, .. } => context.borrow(), - Window::X11 { ref context, .. } => context.borrow(), + Window::Wayland { ref display, .. } => display.borrow(), + Window::X11 { ref display, .. } => display.borrow(), } } } @@ -158,15 +162,33 @@ where let reqs = Default::default(); let window = Rc::new( if native::NativeDisplay::::is_backend(&winit_window) { - let context = - EGLContext::::new(winit_window, attributes, reqs, log.clone())?; - let surface = context.create_surface(())?; - Window::Wayland { context, surface } + let display = EGLDisplay::::new(winit_window, log.clone())?; + let context = display.create_context(attributes, reqs)?; + let surface = display.create_surface( + context.get_pixel_format(), + reqs.double_buffer, + context.get_config_id(), + (), + )?; + Window::Wayland { + display, + context, + surface, + } } else if native::NativeDisplay::::is_backend(&winit_window) { - let context = - EGLContext::::new(winit_window, attributes, reqs, log.clone())?; - let surface = context.create_surface(())?; - Window::X11 { context, surface } + let display = EGLDisplay::::new(winit_window, log.clone())?; + let context = display.create_context(attributes, reqs)?; + let surface = display.create_surface( + context.get_pixel_format(), + reqs.double_buffer, + context.get_config_id(), + (), + )?; + Window::X11 { + display, + context, + surface, + } } else { return Err(Error::NotSupported); }, @@ -263,12 +285,9 @@ impl GLGraphicsBackend for WinitGraphicsBackend { } } - unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { + fn get_proc_address(&self, symbol: &str) -> *const c_void { trace!(self.logger, "Getting symbol for {:?}", symbol); - match *self.window { - Window::Wayland { ref context, .. } => context.get_proc_address(symbol), - Window::X11 { ref context, .. } => context.get_proc_address(symbol), - } + get_proc_address(symbol) } fn get_framebuffer_dimensions(&self) -> (u32, u32) { @@ -281,10 +300,12 @@ impl GLGraphicsBackend for WinitGraphicsBackend { Window::Wayland { ref context, ref surface, + .. } => context.is_current() && surface.is_current(), Window::X11 { ref context, ref surface, + .. } => context.is_current() && surface.is_current(), } } @@ -292,24 +313,33 @@ impl GLGraphicsBackend for WinitGraphicsBackend { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { trace!(self.logger, "Setting EGL context to be the current context"); match *self.window { - Window::Wayland { ref surface, .. } => surface.make_current(), - Window::X11 { ref surface, .. } => surface.make_current(), + Window::Wayland { + ref surface, + ref context, + .. + } => context.make_current_with_surface(surface), + Window::X11 { + ref surface, + ref context, + .. + } => context.make_current_with_surface(surface), } } fn get_pixel_format(&self) -> PixelFormat { match *self.window { - Window::Wayland { ref context, .. } => context.get_pixel_format(), - Window::X11 { ref context, .. } => context.get_pixel_format(), + Window::Wayland { ref surface, .. } => surface.get_pixel_format(), + Window::X11 { ref surface, .. } => surface.get_pixel_format(), } } } +#[cfg(feature = "use_system_lib")] impl EGLGraphicsBackend for WinitGraphicsBackend { - fn bind_wl_display(&self, display: &Display) -> Result { + fn bind_wl_display(&self, wl_display: &Display) -> Result { match *self.window { - Window::Wayland { ref context, .. } => context.bind_wl_display(display), - Window::X11 { ref context, .. } => context.bind_wl_display(display), + Window::Wayland { ref display, .. } => display.bind_wl_display(wl_display), + Window::X11 { ref display, .. } => display.bind_wl_display(wl_display), } } }