From 510c0a54002313ba98519f26098de35922148126 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Sat, 10 Jul 2021 01:44:07 +0200 Subject: [PATCH] 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)]