From d97a9f99705c85ec2a753524f96657313487e70b Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Wed, 7 Jul 2021 20:24:43 +0200 Subject: [PATCH 1/4] egl: Fix egl display handle visibility Currently there is no way to get the raw handle of an `EGLDisplay`, but this can be necessary for implementing additional egl platforms out-of-tree. --- src/backend/egl/display.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index c264b61..44a649a 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -403,6 +403,11 @@ impl EGLDisplay { Ok((desc, config_id)) } + /// Get a handle to the underlying native EGLDisplay + pub fn get_display_handle(&self) -> Arc { + self.display.clone() + } + /// Returns the runtime egl version of this display pub fn get_egl_version(&self) -> (i32, i32) { self.egl_version From 3e2f2afa28f768d22723b5fb9c3a9759f66564e5 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 01:56:42 +0200 Subject: [PATCH 2/4] egl: Let the native surface handle attributes The required surface attributes for egl surface creation might be very dependent on the used egl platform. Therefor let the native surface type handle the attributes instead of deriving them from a set of properties. --- src/backend/egl/context.rs | 4 ---- src/backend/egl/native.rs | 14 +++++++++----- src/backend/egl/surface.rs | 34 +++------------------------------- src/backend/winit.rs | 2 -- 4 files changed, 12 insertions(+), 42 deletions(-) diff --git a/src/backend/egl/context.rs b/src/backend/egl/context.rs index 654e770..92a28c2 100644 --- a/src/backend/egl/context.rs +++ b/src/backend/egl/context.rs @@ -300,9 +300,6 @@ pub struct PixelFormatRequirements { pub depth_bits: Option, /// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`. pub stencil_bits: Option, - /// If `true`, only double-buffered formats will be considered. If `false`, only single-buffer formats. - /// `None` means "don't care". The default is `None`. - pub double_buffer: Option, /// Contains the minimum number of samples per pixel in the color, depth and stencil buffers. /// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled. pub multisampling: Option, @@ -317,7 +314,6 @@ impl Default for PixelFormatRequirements { alpha_bits: Some(8), depth_bits: Some(24), stencil_bits: Some(8), - double_buffer: Some(true), multisampling: None, } } diff --git a/src/backend/egl/native.rs b/src/backend/egl/native.rs index 81d7e9f..f9aedde 100644 --- a/src/backend/egl/native.rs +++ b/src/backend/egl/native.rs @@ -182,7 +182,6 @@ pub unsafe trait EGLNativeSurface: Send + Sync { &self, display: &Arc, config_id: ffi::egl::types::EGLConfig, - surface_attributes: &[c_int], ) -> Result<*const c_void, super::EGLError>; /// Will be called to check if any internal resources will need @@ -224,6 +223,13 @@ pub unsafe trait EGLNativeSurface: Send + Sync { } } +#[cfg(feature = "backend_winit")] +static WINIT_SURFACE_ATTRIBUTES: [c_int; 3] = [ + ffi::egl::RENDER_BUFFER as c_int, + ffi::egl::BACK_BUFFER as c_int, + ffi::egl::NONE as c_int, +]; + #[cfg(feature = "backend_winit")] /// Typed Xlib window for the `X11` backend #[derive(Debug)] @@ -235,7 +241,6 @@ unsafe impl EGLNativeSurface for XlibWindow { &self, display: &Arc, config_id: ffi::egl::types::EGLConfig, - surface_attributes: &[c_int], ) -> Result<*const c_void, super::EGLError> { wrap_egl_call(|| unsafe { let mut id = self.0; @@ -243,7 +248,7 @@ unsafe impl EGLNativeSurface for XlibWindow { display.handle, config_id, (&mut id) as *mut std::os::raw::c_ulong as *mut _, - surface_attributes.as_ptr(), + WINIT_SURFACE_ATTRIBUTES.as_ptr(), ) }) } @@ -255,14 +260,13 @@ unsafe impl EGLNativeSurface for wegl::WlEglSurface { &self, display: &Arc, config_id: ffi::egl::types::EGLConfig, - surface_attributes: &[c_int], ) -> Result<*const c_void, super::EGLError> { wrap_egl_call(|| unsafe { ffi::egl::CreatePlatformWindowSurfaceEXT( display.handle, config_id, self.ptr() as *mut _, - surface_attributes.as_ptr(), + WINIT_SURFACE_ATTRIBUTES.as_ptr(), ) }) } diff --git a/src/backend/egl/surface.rs b/src/backend/egl/surface.rs index 9508dde..bbb4d24 100644 --- a/src/backend/egl/surface.rs +++ b/src/backend/egl/surface.rs @@ -6,8 +6,6 @@ use std::sync::{ Arc, }; -use nix::libc::c_int; - use crate::backend::egl::{ display::{EGLDisplay, EGLDisplayHandle, PixelFormat}, ffi, @@ -15,7 +13,7 @@ use crate::backend::egl::{ EGLError, SwapBuffersError, }; -use slog::{debug, o, trace}; +use slog::{debug, o}; /// EGL surface of a given EGL context for rendering pub struct EGLSurface { @@ -24,7 +22,6 @@ pub struct EGLSurface { pub(crate) surface: AtomicPtr, config_id: ffi::egl::types::EGLConfig, pixel_format: PixelFormat, - surface_attributes: Vec, logger: ::slog::Logger, } @@ -36,7 +33,6 @@ impl fmt::Debug for EGLSurface { .field("surface", &self.surface) .field("config_id", &self.config_id) .field("pixel_format", &self.pixel_format) - .field("surface_attributes", &self.surface_attributes) .field("logger", &self.logger) .finish() } @@ -58,7 +54,6 @@ impl EGLSurface { pub fn new( display: &EGLDisplay, pixel_format: PixelFormat, - double_buffered: Option, config: ffi::egl::types::EGLConfig, native: N, log: L, @@ -69,29 +64,7 @@ impl EGLSurface { { let log = crate::slog_or_fallback(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 = native.create(&display.display, config, &surface_attributes)?; - + let surface = native.create(&display.display, config)?; if surface == ffi::egl::NO_SURFACE { return Err(EGLError::BadSurface); } @@ -102,7 +75,6 @@ impl EGLSurface { surface: AtomicPtr::new(surface as *mut _), config_id: config, pixel_format, - surface_attributes, logger: log, }) } @@ -129,7 +101,7 @@ impl EGLSurface { .compare_exchange( surface, self.native - .create(&self.display, self.config_id, &self.surface_attributes) + .create(&self.display, self.config_id) .map_err(SwapBuffersError::EGLCreateSurface)? as *mut _, Ordering::SeqCst, Ordering::SeqCst, diff --git a/src/backend/winit.rs b/src/backend/winit.rs index e62200a..b88b7c3 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -172,7 +172,6 @@ where EGLSurface::new( &display, context.pixel_format().unwrap(), - reqs.double_buffer, context.config_id(), surface, log.clone(), @@ -183,7 +182,6 @@ where EGLSurface::new( &display, context.pixel_format().unwrap(), - reqs.double_buffer, context.config_id(), xlib_window, log.clone(), From 510c0a54002313ba98519f26098de35922148126 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 01:44:07 +0200 Subject: [PATCH 3/4] egl: Add debug logging Add debug logging to egl via the `EGL_KHR_debug` extension similar to our support for `GL_KHR_debug`. Sadly unlike the GL-extensions the EGL-extension does not let us provide any user-data for the callback, so we fall back to using `slog-stdlog`, when available. Since we only want to initialize this once, when libEGL gets loaded and not per display, but we need to query the list of supported extensions first, some initialization steps where moved around. --- anvil/Cargo.toml | 4 +- anvil/src/main.rs | 2 + src/backend/egl/display.rs | 22 +----- src/backend/egl/error.rs | 2 +- src/backend/egl/ffi.rs | 148 ++++++++++++++++++++++++++++++++++--- 5 files changed, 146 insertions(+), 32 deletions(-) diff --git a/anvil/Cargo.toml b/anvil/Cargo.toml index 2c2fb76..d321b5b 100644 --- a/anvil/Cargo.toml +++ b/anvil/Cargo.toml @@ -14,12 +14,14 @@ rand = "0.7" slog = { version = "2.1.1" } slog-term = "2.8" slog-async = "2.2" +slog-stdlog = "4.1.0" +slog-scope = "4.4.0" xkbcommon = "0.4.0" [dependencies.smithay] path = ".." default-features = false -features = [ "renderer_gl", "backend_egl", "wayland_frontend" ] +features = [ "renderer_gl", "backend_egl", "wayland_frontend", "slog-stdlog" ] [dependencies.x11rb] optional = true diff --git a/anvil/src/main.rs b/anvil/src/main.rs index 257ccab..46183ca 100644 --- a/anvil/src/main.rs +++ b/anvil/src/main.rs @@ -44,6 +44,8 @@ fn main() { //std::sync::Mutex::new(slog_term::term_full().fuse()).fuse(), o!(), ); + let _guard = slog_scope::set_global_logger(log.clone()); + slog_stdlog::init().expect("Could not setup log backend"); let arg = ::std::env::args().nth(1); match arg.as_ref().map(|s| &s[..]) { diff --git a/src/backend/egl/display.rs b/src/backend/egl/display.rs index 44a649a..8ed4074 100644 --- a/src/backend/egl/display.rs +++ b/src/backend/egl/display.rs @@ -75,24 +75,9 @@ pub struct EGLDisplay { fn select_platform_display( native: &N, + dp_extensions: &[String], log: &::slog::Logger, ) -> Result<*const c_void, Error> { - let dp_extensions = unsafe { - let p = wrap_egl_call(|| ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)) - .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported - - // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise - // `eglQueryString` returns an error - if p.is_null() { - return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); - } 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, "Supported EGL client extensions: {:?}", dp_extensions); - for platform in native.supported_platforms() { debug!(log, "Trying EGL platform: {}", platform.platform_name); @@ -160,10 +145,11 @@ impl EGLDisplay { L: Into>, { let log = crate::slog_or_fallback(logger.into()).new(o!("smithay_module" => "backend_egl")); - ffi::make_sure_egl_is_loaded(); + let dp_extensions = ffi::make_sure_egl_is_loaded()?; + debug!(log, "Supported EGL client extensions: {:?}", dp_extensions); // we create an EGLDisplay - let display = select_platform_display(native, &log)?; + let display = select_platform_display(native, &dp_extensions, &log)?; // We can then query the egl api version let egl_version = { diff --git a/src/backend/egl/error.rs b/src/backend/egl/error.rs index 6690e8f..5ff1843 100644 --- a/src/backend/egl/error.rs +++ b/src/backend/egl/error.rs @@ -127,7 +127,7 @@ impl From for EGLError { } impl EGLError { - fn from_last_call() -> Result<(), EGLError> { + pub(super) fn from_last_call() -> Result<(), EGLError> { match unsafe { ffi::egl::GetError() as u32 } { ffi::egl::SUCCESS => Ok(()), x => Err(EGLError::from(x)), diff --git a/src/backend/egl/ffi.rs b/src/backend/egl/ffi.rs index caa7c94..e40867c 100644 --- a/src/backend/egl/ffi.rs +++ b/src/backend/egl/ffi.rs @@ -1,11 +1,14 @@ #![allow(missing_docs)] +use super::Error; use nix::libc::{c_long, c_uint, c_void}; pub type khronos_utime_nanoseconds_t = khronos_uint64_t; pub type khronos_uint64_t = u64; pub type khronos_ssize_t = c_long; pub type EGLint = i32; +pub type EGLchar = char; +pub type EGLLabelKHR = *const c_void; pub type EGLNativeDisplayType = NativeDisplayType; pub type EGLNativePixmapType = NativePixmapType; pub type EGLNativeWindowType = NativeWindowType; @@ -13,17 +16,46 @@ pub type NativeDisplayType = *const c_void; pub type NativePixmapType = *const c_void; pub type NativeWindowType = *const c_void; -pub fn make_sure_egl_is_loaded() { - use std::{ffi::CString, ptr}; +extern "system" fn egl_debug_log( + severity: egl::types::EGLenum, + command: *const EGLchar, + _id: EGLint, + _thread: EGLLabelKHR, + _obj: EGLLabelKHR, + message: *const EGLchar, +) { + let _ = std::panic::catch_unwind(move || unsafe { + let msg = std::ffi::CStr::from_ptr(message as *const _); + let message_utf8 = msg.to_string_lossy(); + let cmd = std::ffi::CStr::from_ptr(command as *const _); + let command_utf8 = cmd.to_string_lossy(); + let logger = crate::slog_or_fallback(None).new(slog::o!("backend" => "egl")); + match severity { + egl::DEBUG_MSG_CRITICAL_KHR | egl::DEBUG_MSG_ERROR_KHR => { + slog::error!(logger, "[EGL] {}: {}", command_utf8, message_utf8) + } + egl::DEBUG_MSG_WARN_KHR => slog::warn!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + egl::DEBUG_MSG_INFO_KHR => slog::info!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + _ => slog::debug!(logger, "[EGL] {}: {}", command_utf8, message_utf8), + }; + }); +} + +pub fn make_sure_egl_is_loaded() -> Result, Error> { + use std::{ + ffi::{CStr, CString}, + ptr, + }; + + fn constrain(f: F) -> F + where + F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, + { + f + } + let proc_address = constrain(|sym| unsafe { super::get_proc_address(sym) }); egl::LOAD.call_once(|| unsafe { - fn constrain(f: F) -> F - where - F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, - { - f - } - egl::load_with(|sym| { let name = CString::new(sym).unwrap(); let symbol = egl::LIB.get::<*mut c_void>(name.as_bytes()); @@ -32,14 +64,47 @@ pub fn make_sure_egl_is_loaded() { Err(_) => ptr::null(), } }); - let proc_address = constrain(|sym| super::get_proc_address(sym)); egl::load_with(&proc_address); egl::BindWaylandDisplayWL::load_with(&proc_address); egl::UnbindWaylandDisplayWL::load_with(&proc_address); egl::QueryWaylandBufferWL::load_with(&proc_address); - #[cfg(feature = "backend_drm_eglstream")] - egl::StreamConsumerAcquireAttribNV::load_with(&proc_address); + egl::DebugMessageControlKHR::load_with(&proc_address); }); + + let extensions = unsafe { + let p = super::wrap_egl_call(|| egl::QueryString(egl::NO_DISPLAY, egl::EXTENSIONS as i32)) + .map_err(Error::InitFailed)?; //TODO EGL_EXT_client_extensions not supported + + // this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise + // `eglQueryString` returns an error + if p.is_null() { + return Err(Error::EglExtensionNotSupported(&["EGL_EXT_platform_base"])); + } 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::>() + } + }; + + egl::DEBUG.call_once(|| unsafe { + if extensions.iter().any(|ext| ext == "EGL_KHR_debug") { + let debug_attribs = [ + egl::DEBUG_MSG_CRITICAL_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_ERROR_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_WARN_KHR as isize, + egl::TRUE as isize, + egl::DEBUG_MSG_INFO_KHR as isize, + egl::TRUE as isize, + egl::NONE as isize, + ]; + // we do not check for success, because there is not much we can do otherwise. + egl::DebugMessageControlKHR(Some(egl_debug_log), debug_attribs.as_ptr()); + } + }); + + Ok(extensions) } #[allow(clippy::all, missing_debug_implementations)] @@ -53,9 +118,40 @@ pub mod egl { } pub static LOAD: Once = Once::new(); + pub static DEBUG: Once = Once::new(); include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + type EGLDEBUGPROCKHR = Option< + extern "system" fn( + _error: egl::types::EGLenum, + command: *const EGLchar, + _id: EGLint, + _thread: EGLLabelKHR, + _obj: EGLLabelKHR, + message: *const EGLchar, + ), + >; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_CRITICAL_KHR: types::EGLenum = 0x33B9; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_ERROR_KHR: types::EGLenum = 0x33BA; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_INFO_KHR: types::EGLenum = 0x33BC; + #[allow(dead_code, non_upper_case_globals)] + pub const DEBUG_MSG_WARN_KHR: types::EGLenum = 0x33BB; + + #[allow(non_snake_case, unused_variables, dead_code)] + #[inline] + pub unsafe fn DebugMessageControlKHR( + callback: EGLDEBUGPROCKHR, + attrib_list: *const types::EGLAttrib, + ) -> types::EGLint { + __gl_imports::mem::transmute::< + _, + extern "system" fn(EGLDEBUGPROCKHR, *const types::EGLAttrib) -> types::EGLint, + >(wayland_storage::DebugMessageControlKHR.f)(callback, attrib_list) + } /* * `gl_generator` cannot generate bindings for the `EGL_WL_bind_wayland_display` extension. * Lets do it ourselves... @@ -118,6 +214,34 @@ pub mod egl { f: super::missing_fn_panic as *const raw::c_void, is_loaded: false, }; + pub static mut DebugMessageControlKHR: FnPtr = FnPtr { + f: super::missing_fn_panic as *const raw::c_void, + is_loaded: false, + }; + } + + #[allow(non_snake_case)] + pub mod DebugMessageControlKHR { + use super::FnPtr; + use super::__gl_imports::raw; + use super::{metaloadfn, wayland_storage}; + + #[inline] + #[allow(dead_code)] + pub fn is_loaded() -> bool { + unsafe { wayland_storage::DebugMessageControlKHR.is_loaded } + } + + #[allow(dead_code)] + pub fn load_with(mut loadfn: F) + where + F: FnMut(&'static str) -> *const raw::c_void, + { + unsafe { + wayland_storage::DebugMessageControlKHR = + FnPtr::new(metaloadfn(&mut loadfn, "eglDebugMessageControlKHR", &[])) + } + } } #[allow(non_snake_case)] From 4649a34f77038ffb5bf17085dcd8745c2dbc8e1b Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 19:55:49 +0200 Subject: [PATCH 4/4] anvil: Fix initialization when `bind_wl_display` fails. If the `egl`-feature is enabled, but `bind_wl_display` fails (e.g. when software rendering via llvmpipe is in use), we should not fail to start. --- anvil/src/udev.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index 3798eae..ac7d75c 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -441,10 +441,13 @@ impl AnvilState { #[cfg(feature = "egl")] if path.canonicalize().ok() == self.backend_data.primary_gpu { info!(self.log, "Initializing EGL Hardware Acceleration via {:?}", path); - renderer + if renderer .borrow_mut() .bind_wl_display(&*self.display.borrow()) - .expect("Unable to bind Wl Display?"); + .is_ok() + { + info!(self.log, "EGL hardware-acceleration enabled"); + } } let backends = Rc::new(RefCell::new(scan_connectors(