From 6d5528e51c41cee68d93202c6188e6948024176d Mon Sep 17 00:00:00 2001 From: Nimi Wariboko Jr Date: Mon, 21 Dec 2020 13:59:21 -0800 Subject: [PATCH 1/3] [gl] add support for wayland display servers --- src/backend/gl/src/window/egl.rs | 330 +++++++++++++++++++++++-------- 1 file changed, 252 insertions(+), 78 deletions(-) diff --git a/src/backend/gl/src/window/egl.rs b/src/backend/gl/src/window/egl.rs index 2efb5d94b09..fba57eaac4b 100644 --- a/src/backend/gl/src/window/egl.rs +++ b/src/backend/gl/src/window/egl.rs @@ -3,7 +3,7 @@ use crate::{conv, native, GlContainer, PhysicalDevice, Starc}; use glow::HasContext; use hal::{image, window as w}; -use std::{os::raw, ptr}; +use std::{os::raw, ptr, sync::Mutex}; #[derive(Debug)] pub struct Swapchain { @@ -18,67 +18,57 @@ pub struct Swapchain { #[derive(Debug)] pub struct Instance { egl: Starc, - display: egl::Display, + wsi_library: Option, + inner: Mutex, +} + +#[derive(Debug)] +pub struct Inner { + egl: Starc, version: (i32, i32), + supports_native_window: bool, + display: egl::Display, config: egl::Config, context: egl::Context, - supports_native_window: bool, - wsi_library: Option, + wl_display: Option<*mut raw::c_void>, } unsafe impl Send for Instance {} unsafe impl Sync for Instance {} +const EGL_PLATFORM_WAYLAND_KHR: u32 = 0x31D8; +const EGL_PLATFORM_X11_KHR: u32 = 0x31D5; + type XOpenDisplayFun = unsafe extern "system" fn(display_name: *const raw::c_char) -> *mut raw::c_void; -impl hal::Instance for Instance { - fn create(_: &str, _: u32) -> Result { - let egl = match unsafe { egl::DynamicInstance::load() } { - Ok(egl) => Starc::new(egl), - Err(e) => { - log::warn!("Unable to open libEGL.so: {:?}", e); - return Err(hal::UnsupportedBackend); - } - }; - - let client_extensions = egl - .query_string(None, egl::EXTENSIONS) - .map_err(|_| hal::UnsupportedBackend)? - .to_string_lossy(); - log::info!("Client extensions: {:?}", client_extensions); - let client_ext_list = client_extensions.split_whitespace().collect::>(); - - let mut wsi_library = None; - let x11_display = if client_ext_list.contains(&"EGL_EXT_platform_x11") { - log::info!("Loading X11 library to get the current display"); - if let Ok(library) = libloading::Library::new("libX11.so") { - let func: libloading::Symbol = - unsafe { library.get(b"XOpenDisplay").unwrap() }; - let result = unsafe { func(ptr::null()) }; - wsi_library = Some(library); - ptr::NonNull::new(result) - } else { - None - } - } else { - None - }; - let display = if let Some(x11_display) = x11_display { - log::info!("Using X11 platform"); - const EGL_PLATFORM_X11_KHR: u32 = 0x31D5; - let display_attributes = [egl::ATTRIB_NONE]; - egl.get_platform_display( - EGL_PLATFORM_X11_KHR, - x11_display.as_ptr(), - &display_attributes, - ) - .unwrap() - } else { - log::info!("Using default platform"); - egl.get_display(egl::DEFAULT_DISPLAY).unwrap() - }; +type WlDisplayConnectFun = + unsafe extern "system" fn(display_name: *const raw::c_char) -> *mut raw::c_void; +type WlDisplayDisconnectFun = unsafe extern "system" fn(display: *const raw::c_void); + +type WlEglWindowCreateFun = unsafe extern "system" fn( + surface: *const raw::c_void, + width: raw::c_int, + height: raw::c_int, +) -> *mut raw::c_void; + +type WlEglWindowResizeFun = unsafe extern "system" fn( + window: *const raw::c_void, + width: raw::c_int, + height: raw::c_int, + dx: raw::c_int, + dy: raw::c_int, +); + +type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void); + +impl Inner { + fn create( + egl: Starc, + display: egl::Display, + wsi_library: Option<&libloading::Library>, + ) -> Result { let version = egl .initialize(display) .map_err(|_| hal::UnsupportedBackend)?; @@ -177,20 +167,132 @@ impl hal::Instance for Instance { } }; - Ok(Instance { + Ok(Self { egl, display, version, + supports_native_window, config, context, - supports_native_window, + wl_display: None, + }) + } +} + +impl Drop for Inner { + fn drop(&mut self) { + if let Err(e) = self.egl.destroy_context(self.display, self.context) { + log::warn!("Error in destroy_context: {:?}", e); + } + if let Err(e) = self.egl.terminate(self.display) { + log::warn!("Error in terminate: {:?}", e); + } + } +} + +impl hal::Instance for Instance { + fn create(_: &str, _: u32) -> Result { + let egl = match unsafe { egl::DynamicInstance::load() } { + Ok(egl) => Starc::new(egl), + Err(e) => { + log::warn!("Unable to open libEGL.so: {:?}", e); + return Err(hal::UnsupportedBackend); + } + }; + + let client_extensions = egl + .query_string(None, egl::EXTENSIONS) + .map_err(|_| hal::UnsupportedBackend)? + .to_string_lossy(); + log::info!("Client extensions: {:?}", client_extensions); + let client_ext_list = client_extensions.split_whitespace().collect::>(); + + let mut wsi_library = None; + + let wayland_display = if client_ext_list.contains(&"EGL_EXT_platform_wayland") { + log::info!("Loading Wayland library to get the current display"); + if let Ok(library) = libloading::Library::new("libwayland-client.so") { + /* We try to connect and disconnect here to simply ensure there + * is an active wayland display available. + */ + let wl_display_connect: libloading::Symbol = + unsafe { library.get(b"wl_display_connect").unwrap() }; + let wl_display_disconnect: libloading::Symbol = + unsafe { library.get(b"wl_display_disconnect").unwrap() }; + if let Some(display) = ptr::NonNull::new(unsafe { wl_display_connect(ptr::null()) }) + { + unsafe { wl_display_disconnect(display.as_ptr()) } + if let Ok(library) = libloading::Library::new("libwayland-egl.so") { + Some(((), library)) + } else { + None + } + } else { + None + } + } else { + None + } + } else { + None + }; + + let x11_display = if client_ext_list.contains(&"EGL_EXT_platform_x11") { + log::info!("Loading X11 library to get the current display"); + if let Ok(library) = libloading::Library::new("libX11.so") { + let func: libloading::Symbol = + unsafe { library.get(b"XOpenDisplay").unwrap() }; + let result = unsafe { func(ptr::null()) }; + ptr::NonNull::new(result).map(|ptr| (ptr, library)) + } else { + None + } + } else { + None + }; + + let display = match (wayland_display, x11_display) { + (Some((_, library)), _) => { + log::info!("Using Wayland platform"); + let display_attributes = [egl::ATTRIB_NONE]; + wsi_library = Some(library); + egl.get_platform_display( + EGL_PLATFORM_WAYLAND_KHR, + egl::DEFAULT_DISPLAY, + &display_attributes, + ) + .unwrap() + } + (_, Some((x11_display, library))) => { + log::info!("Using X11 platform"); + let display_attributes = [egl::ATTRIB_NONE]; + wsi_library = Some(library); + egl.get_platform_display( + EGL_PLATFORM_X11_KHR, + x11_display.as_ptr(), + &display_attributes, + ) + .unwrap() + } + _ => { + log::info!("Using default platform"); + egl.get_display(egl::DEFAULT_DISPLAY).unwrap() + } + }; + + let inner = Inner::create(egl.clone(), display, wsi_library.as_ref())?; + + Ok(Instance { + egl, + inner: Mutex::new(inner), wsi_library, }) } fn enumerate_adapters(&self) -> Vec> { + let inner = self.inner.lock().unwrap(); self.egl - .make_current(self.display, None, None, Some(self.context)) + .make_current(inner.display, None, None, Some(inner.context)) .unwrap(); let context = unsafe { glow::Context::from_loader_function(|name| { @@ -206,6 +308,8 @@ impl hal::Instance for Instance { has_handle: &impl raw_window_handle::HasRawWindowHandle, ) -> Result { use raw_window_handle::RawWindowHandle as Rwh; + + let mut inner = self.inner.lock().unwrap(); let mut native_window = match has_handle.raw_window_handle() { #[cfg(not(target_os = "android"))] Rwh::Xlib(handle) => handle.window, @@ -213,28 +317,83 @@ impl hal::Instance for Instance { Rwh::Xcb(handle) => handle.window as _, #[cfg(target_os = "android")] Rwh::Android(handle) => handle.a_native_window, + #[cfg(not(target_os = "android"))] + Rwh::Wayland(handle) => { + /* Wayland displays are not sharable between surfaces so if the + * surface we receive from this handle is from a different + * display, we must re-initialize the context. + * + * See gfx-rs/gfx#3545 + */ + if inner + .wl_display + .map(|ptr| ptr != handle.display) + .unwrap_or(true) + { + use std::ops::DerefMut; + let display_attributes = [egl::ATTRIB_NONE]; + let display = self + .egl + .get_platform_display( + EGL_PLATFORM_WAYLAND_KHR, + handle.display, + &display_attributes, + ) + .unwrap(); + + let new_inner = + Inner::create(self.egl.clone(), display, self.wsi_library.as_ref()) + .map_err(|_| w::InitError::UnsupportedWindowHandle)?; + + let old_inner = std::mem::replace(inner.deref_mut(), new_inner); + inner.wl_display = Some(handle.display); + drop(old_inner); + } + + let window = { + let wl_egl_window_create: libloading::Symbol = self + .wsi_library + .as_ref() + .expect("unsupported window") + .get(b"wl_egl_window_create") + .unwrap(); + let result = wl_egl_window_create(handle.surface, 640, 480); + ptr::NonNull::new(result) + }; + window.expect("unsupported window").as_ptr() as _ + } other => panic!("Unsupported window: {:?}", other), }; - let attributes = [ - egl::RENDER_BUFFER as usize, - egl::SINGLE_BUFFER as usize, - // Always enable sRGB - egl::GL_COLORSPACE as usize, - egl::GL_COLORSPACE_SRGB as usize, - egl::ATTRIB_NONE, - ]; + let mut attributes = vec![egl::RENDER_BUFFER as usize, egl::SINGLE_BUFFER as usize]; + if inner.version >= (1, 5) { + // Always enable sRGB in EGL 1.5 + attributes.push(egl::GL_COLORSPACE as usize); + attributes.push(egl::GL_COLORSPACE_SRGB as usize); + } + attributes.push(egl::ATTRIB_NONE); + + let native_window = match has_handle.raw_window_handle() { + #[cfg(not(target_os = "android"))] + Rwh::Wayland(_) => native_window as *mut raw::c_void, + _ => &mut native_window as *mut _ as *mut _, + }; match self.egl.create_platform_window_surface( - self.display, - self.config, - &mut native_window as *mut _ as *mut _, + inner.display, + inner.config, + native_window, &attributes, ) { Ok(raw) => Ok(Surface { egl: self.egl.clone(), raw, - display: self.display, - context: self.context, - presentable: self.supports_native_window, + display: inner.display, + context: inner.context, + presentable: inner.supports_native_window, + wl_window: match has_handle.raw_window_handle() { + #[cfg(not(target_os = "android"))] + Rwh::Wayland(_) => Some(native_window), + _ => None, + }, swapchain: None, }), Err(e) => { @@ -245,17 +404,18 @@ impl hal::Instance for Instance { } unsafe fn destroy_surface(&self, surface: Surface) { - self.egl.destroy_surface(self.display, surface.raw).unwrap(); - } -} - -impl Drop for Instance { - fn drop(&mut self) { - if let Err(e) = self.egl.destroy_context(self.display, self.context) { - log::warn!("Error in destroy_context: {:?}", e); - } - if let Err(e) = self.egl.terminate(self.display) { - log::warn!("Error in terminate: {:?}", e); + let inner = self.inner.lock().unwrap(); + self.egl + .destroy_surface(inner.display, surface.raw) + .unwrap(); + if let Some(wl_window) = surface.wl_window { + let wl_egl_window_destroy: libloading::Symbol = self + .wsi_library + .as_ref() + .expect("unsupported window") + .get(b"wl_egl_window_destroy") + .unwrap(); + wl_egl_window_destroy(wl_window) } } } @@ -267,6 +427,7 @@ pub struct Surface { display: egl::Display, context: egl::Context, presentable: bool, + wl_window: Option<*mut raw::c_void>, pub(crate) swapchain: Option, } @@ -283,6 +444,19 @@ impl w::PresentationSurface for Surface { ) -> Result<(), w::SwapchainError> { self.unconfigure_swapchain(device); + if let Some(window) = self.wl_window { + let library = libloading::Library::new("libwayland-egl.so").unwrap(); + let wl_egl_window_resize: libloading::Symbol = + library.get(b"wl_egl_window_resize").unwrap(); + wl_egl_window_resize( + window, + config.extent.width as i32, + config.extent.height as i32, + 0, + 0, + ); + } + let desc = conv::describe_format(config.format).unwrap(); let gl = &device.share.context; From 8b55a715b167e2054083bcf3bfb17a45898208b6 Mon Sep 17 00:00:00 2001 From: Nimi Wariboko Jr Date: Tue, 22 Dec 2020 12:14:28 -0800 Subject: [PATCH 2/3] [gl] Cleanup Instance fields; Refactor nested statements --- src/backend/gl/src/window/egl.rs | 133 ++++++++++++++----------------- 1 file changed, 62 insertions(+), 71 deletions(-) diff --git a/src/backend/gl/src/window/egl.rs b/src/backend/gl/src/window/egl.rs index fba57eaac4b..a80f2f41c3e 100644 --- a/src/backend/gl/src/window/egl.rs +++ b/src/backend/gl/src/window/egl.rs @@ -17,7 +17,6 @@ pub struct Swapchain { #[derive(Debug)] pub struct Instance { - egl: Starc, wsi_library: Option, inner: Mutex, } @@ -63,6 +62,31 @@ type WlEglWindowResizeFun = unsafe extern "system" fn( type WlEglWindowDestroyFun = unsafe extern "system" fn(window: *const raw::c_void); +fn open_x_display() -> Option<(ptr::NonNull, libloading::Library)> { + log::info!("Loading X11 library to get the current display"); + let library = libloading::Library::new("libX11.so").ok()?; + let func: libloading::Symbol = + unsafe { library.get(b"XOpenDisplay").unwrap() }; + let result = unsafe { func(ptr::null()) }; + ptr::NonNull::new(result).map(|ptr| (ptr, library)) +} + +fn test_wayland_display() -> Option { + /* We try to connect and disconnect here to simply ensure there + * is an active wayland display available. + */ + log::info!("Loading Wayland library to get the current display"); + let client_library = libloading::Library::new("libwayland-client.so").ok()?; + let wl_display_connect: libloading::Symbol = + unsafe { client_library.get(b"wl_display_connect").unwrap() }; + let wl_display_disconnect: libloading::Symbol = + unsafe { client_library.get(b"wl_display_disconnect").unwrap() }; + let display = ptr::NonNull::new(unsafe { wl_display_connect(ptr::null()) })?; + unsafe { wl_display_disconnect(display.as_ptr()) }; + let library = libloading::Library::new("libwayland-egl.so").ok()?; + Some(library) +} + impl Inner { fn create( egl: Starc, @@ -210,80 +234,45 @@ impl hal::Instance for Instance { let mut wsi_library = None; let wayland_display = if client_ext_list.contains(&"EGL_EXT_platform_wayland") { - log::info!("Loading Wayland library to get the current display"); - if let Ok(library) = libloading::Library::new("libwayland-client.so") { - /* We try to connect and disconnect here to simply ensure there - * is an active wayland display available. - */ - let wl_display_connect: libloading::Symbol = - unsafe { library.get(b"wl_display_connect").unwrap() }; - let wl_display_disconnect: libloading::Symbol = - unsafe { library.get(b"wl_display_disconnect").unwrap() }; - if let Some(display) = ptr::NonNull::new(unsafe { wl_display_connect(ptr::null()) }) - { - unsafe { wl_display_disconnect(display.as_ptr()) } - if let Ok(library) = libloading::Library::new("libwayland-egl.so") { - Some(((), library)) - } else { - None - } - } else { - None - } - } else { - None - } + test_wayland_display() } else { None }; let x11_display = if client_ext_list.contains(&"EGL_EXT_platform_x11") { - log::info!("Loading X11 library to get the current display"); - if let Ok(library) = libloading::Library::new("libX11.so") { - let func: libloading::Symbol = - unsafe { library.get(b"XOpenDisplay").unwrap() }; - let result = unsafe { func(ptr::null()) }; - ptr::NonNull::new(result).map(|ptr| (ptr, library)) - } else { - None - } + open_x_display() } else { None }; - let display = match (wayland_display, x11_display) { - (Some((_, library)), _) => { - log::info!("Using Wayland platform"); - let display_attributes = [egl::ATTRIB_NONE]; - wsi_library = Some(library); - egl.get_platform_display( - EGL_PLATFORM_WAYLAND_KHR, - egl::DEFAULT_DISPLAY, - &display_attributes, - ) - .unwrap() - } - (_, Some((x11_display, library))) => { - log::info!("Using X11 platform"); - let display_attributes = [egl::ATTRIB_NONE]; - wsi_library = Some(library); - egl.get_platform_display( - EGL_PLATFORM_X11_KHR, - x11_display.as_ptr(), - &display_attributes, - ) - .unwrap() - } - _ => { - log::info!("Using default platform"); - egl.get_display(egl::DEFAULT_DISPLAY).unwrap() - } + let display = if let Some(library) = wayland_display { + log::info!("Using Wayland platform"); + let display_attributes = [egl::ATTRIB_NONE]; + wsi_library = Some(library); + egl.get_platform_display( + EGL_PLATFORM_WAYLAND_KHR, + egl::DEFAULT_DISPLAY, + &display_attributes, + ) + .unwrap() + } else if let Some((x11_display, library)) = x11_display { + log::info!("Using X11 platform"); + let display_attributes = [egl::ATTRIB_NONE]; + wsi_library = Some(library); + egl.get_platform_display( + EGL_PLATFORM_X11_KHR, + x11_display.as_ptr(), + &display_attributes, + ) + .unwrap() + } else { + log::info!("Using default platform"); + egl.get_display(egl::DEFAULT_DISPLAY).unwrap() }; let inner = Inner::create(egl.clone(), display, wsi_library.as_ref())?; Ok(Instance { - egl, inner: Mutex::new(inner), wsi_library, }) @@ -291,12 +280,13 @@ impl hal::Instance for Instance { fn enumerate_adapters(&self) -> Vec> { let inner = self.inner.lock().unwrap(); - self.egl + inner + .egl .make_current(inner.display, None, None, Some(inner.context)) .unwrap(); let context = unsafe { glow::Context::from_loader_function(|name| { - self.egl.get_proc_address(name).unwrap() as *const _ + inner.egl.get_proc_address(name).unwrap() as *const _ }) }; // Create physical device @@ -332,7 +322,7 @@ impl hal::Instance for Instance { { use std::ops::DerefMut; let display_attributes = [egl::ATTRIB_NONE]; - let display = self + let display = inner .egl .get_platform_display( EGL_PLATFORM_WAYLAND_KHR, @@ -342,7 +332,7 @@ impl hal::Instance for Instance { .unwrap(); let new_inner = - Inner::create(self.egl.clone(), display, self.wsi_library.as_ref()) + Inner::create(inner.egl.clone(), display, self.wsi_library.as_ref()) .map_err(|_| w::InitError::UnsupportedWindowHandle)?; let old_inner = std::mem::replace(inner.deref_mut(), new_inner); @@ -372,26 +362,26 @@ impl hal::Instance for Instance { } attributes.push(egl::ATTRIB_NONE); - let native_window = match has_handle.raw_window_handle() { + let native_window_ptr = match has_handle.raw_window_handle() { #[cfg(not(target_os = "android"))] Rwh::Wayland(_) => native_window as *mut raw::c_void, _ => &mut native_window as *mut _ as *mut _, }; - match self.egl.create_platform_window_surface( + match inner.egl.create_platform_window_surface( inner.display, inner.config, - native_window, + native_window_ptr, &attributes, ) { Ok(raw) => Ok(Surface { - egl: self.egl.clone(), + egl: inner.egl.clone(), raw, display: inner.display, context: inner.context, presentable: inner.supports_native_window, wl_window: match has_handle.raw_window_handle() { #[cfg(not(target_os = "android"))] - Rwh::Wayland(_) => Some(native_window), + Rwh::Wayland(_) => Some(native_window_ptr), _ => None, }, swapchain: None, @@ -405,7 +395,8 @@ impl hal::Instance for Instance { unsafe fn destroy_surface(&self, surface: Surface) { let inner = self.inner.lock().unwrap(); - self.egl + inner + .egl .destroy_surface(inner.display, surface.raw) .unwrap(); if let Some(wl_window) = surface.wl_window { From 9f7be918b0889f2276a20a1f1a4ff0dd795f363e Mon Sep 17 00:00:00 2001 From: Nimi Wariboko Jr Date: Tue, 22 Dec 2020 16:40:14 -0800 Subject: [PATCH 3/3] [gl] prefer to use parking_lot::Mutex rather than stdlib --- src/backend/gl/src/window/egl.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/backend/gl/src/window/egl.rs b/src/backend/gl/src/window/egl.rs index a80f2f41c3e..a17087b31b7 100644 --- a/src/backend/gl/src/window/egl.rs +++ b/src/backend/gl/src/window/egl.rs @@ -3,7 +3,8 @@ use crate::{conv, native, GlContainer, PhysicalDevice, Starc}; use glow::HasContext; use hal::{image, window as w}; -use std::{os::raw, ptr, sync::Mutex}; +use parking_lot::Mutex; +use std::{os::raw, ptr}; #[derive(Debug)] pub struct Swapchain { @@ -279,7 +280,7 @@ impl hal::Instance for Instance { } fn enumerate_adapters(&self) -> Vec> { - let inner = self.inner.lock().unwrap(); + let inner = self.inner.lock(); inner .egl .make_current(inner.display, None, None, Some(inner.context)) @@ -299,7 +300,7 @@ impl hal::Instance for Instance { ) -> Result { use raw_window_handle::RawWindowHandle as Rwh; - let mut inner = self.inner.lock().unwrap(); + let mut inner = self.inner.lock(); let mut native_window = match has_handle.raw_window_handle() { #[cfg(not(target_os = "android"))] Rwh::Xlib(handle) => handle.window, @@ -394,7 +395,7 @@ impl hal::Instance for Instance { } unsafe fn destroy_surface(&self, surface: Surface) { - let inner = self.inner.lock().unwrap(); + let inner = self.inner.lock(); inner .egl .destroy_surface(inner.display, surface.raw)