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/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( 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/display.rs b/src/backend/egl/display.rs index c264b61..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 = { @@ -403,6 +389,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 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)] 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(),