From 2475969eca5bd055e5852d3a03df83790e165d16 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Fri, 29 Nov 2019 18:05:09 -0800 Subject: [PATCH] window: enable egl support for wayland --- window/Cargo.toml | 6 +- window/examples/basic_opengl.rs | 1 + window/src/egl.rs | 179 ++++++++++++++++++++-------- window/src/os/wayland/connection.rs | 2 +- window/src/os/wayland/window.rs | 64 ++++++++-- 5 files changed, 186 insertions(+), 66 deletions(-) diff --git a/window/Cargo.toml b/window/Cargo.toml index 0affcb630..1a56e6016 100644 --- a/window/Cargo.toml +++ b/window/Cargo.toml @@ -8,6 +8,9 @@ description = "Cross platform window setup and render" license = "MIT" build = "build.rs" +[dev-dependencies] +pretty_env_logger = "0.3" + [build-dependencies] gl_generator = {version="0.13", optional=true} @@ -28,7 +31,7 @@ glium = { version = "0.26.0-alpha3", optional=true, default-features = false} [features] async_await = [] opengl = ["cgl", "glium", "gl_generator", "libloading"] -wayland = ["smithay-client-toolkit", "memmap"] +wayland = ["smithay-client-toolkit", "memmap", "wayland-client"] [target."cfg(windows)".dependencies] winapi = { version = "0.3", features = [ @@ -53,6 +56,7 @@ mio-extras = "2.0" libc = "0.2" smithay-client-toolkit = {version = "0.6", optional = true} memmap = {version="0.7", optional=true} +wayland-client = {version="0.23", optional=true, features=["egl"]} [target.'cfg(target_os="macos")'.dependencies] cocoa = "0.20" diff --git a/window/examples/basic_opengl.rs b/window/examples/basic_opengl.rs index aace776cc..5662516e0 100644 --- a/window/examples/basic_opengl.rs +++ b/window/examples/basic_opengl.rs @@ -69,6 +69,7 @@ fn spawn_window() -> Fallible<()> { } fn main() -> Fallible<()> { + pretty_env_logger::init(); let conn = Connection::init()?; spawn_window()?; conn.run_message_loop() diff --git a/window/src/egl.rs b/window/src/egl.rs index 7162d20b0..d5c961451 100644 --- a/window/src/egl.rs +++ b/window/src/egl.rs @@ -199,10 +199,7 @@ impl EglWrapper { } impl GlState { - pub fn create( - display: Option, - window: ffi::EGLNativeWindowType, - ) -> Fallible { + fn with_egl_lib Fallible>(mut func: F) -> Fallible { let paths = [ // While EGL is cross platform, it isn't available on macOS nor is it // available on my nvidia based system @@ -216,61 +213,137 @@ impl GlState { "libEGL.so", ]; for path in &paths { - eprintln!("trying {}", path); if let Ok(lib) = libloading::Library::new(path) { - if let Ok(egl) = EglWrapper::load_egl(lib) { - let egl_display = egl.get_display(display)?; - - let (major, minor) = egl.initialize_and_get_version(egl_display)?; - eprintln!("initialized EGL version {}.{}", major, minor); - - let configs = egl.choose_config( - egl_display, - &[ - ffi::ALPHA_SIZE, - 8, - ffi::RED_SIZE, - 8, - ffi::GREEN_SIZE, - 8, - ffi::BLUE_SIZE, - 8, - ffi::DEPTH_SIZE, - 24, - ffi::CONFORMANT, - ffi::OPENGL_ES3_BIT, - ffi::RENDERABLE_TYPE, - ffi::OPENGL_ES3_BIT, - ffi::SURFACE_TYPE, - ffi::WINDOW_BIT | ffi::PBUFFER_BIT | ffi::PIXMAP_BIT, - ffi::NONE, - ], - )?; - - let first_config = *configs.first().ok_or_else(|| { - failure::err_msg("no compatible EGL configuration was found") - })?; - - let surface = egl.create_window_surface(egl_display, first_config, window)?; - - let context = egl.create_context( - egl_display, - first_config, - std::ptr::null(), - &[ffi::CONTEXT_MAJOR_VERSION, 3, ffi::NONE], - )?; - - return Ok(Self { - egl, - display: egl_display, - context, - surface, - }); + match EglWrapper::load_egl(lib) { + Ok(egl) => match func(egl) { + Ok(result) => { + log::error!("initialized {}", path); + return Ok(result); + } + Err(e) => { + log::error!("with_egl_lib failed: {}", e); + } + }, + Err(e) => { + log::error!("load_egl failed: {}", e); + } } } } failure::bail!("EGL library not found") } + + #[cfg(all(unix, feature = "wayland", not(target_os = "macos")))] + pub fn create_wayland( + display: Option, + wegl_surface: &wayland_client::egl::WlEglSurface, + ) -> Fallible { + Self::with_egl_lib(move |egl| { + let egl_display = egl.get_display(display)?; + + let (major, minor) = egl.initialize_and_get_version(egl_display)?; + log::error!("initialized EGL version {}.{}", major, minor); + + let configs = egl.choose_config( + egl_display, + &[ + ffi::ALPHA_SIZE, + 8, + ffi::RED_SIZE, + 8, + ffi::GREEN_SIZE, + 8, + ffi::BLUE_SIZE, + 8, + ffi::DEPTH_SIZE, + 24, + ffi::CONFORMANT, + ffi::OPENGL_ES3_BIT, + ffi::RENDERABLE_TYPE, + ffi::OPENGL_ES3_BIT, + ffi::SURFACE_TYPE, + ffi::WINDOW_BIT, // | ffi::PBUFFER_BIT | ffi::PIXMAP_BIT, + ffi::NONE, + ], + )?; + + let first_config = *configs + .first() + .ok_or_else(|| failure::err_msg("no compatible EGL configuration was found"))?; + + let window = wegl_surface.ptr(); + let surface = egl.create_window_surface(egl_display, first_config, window)?; + + let context = egl.create_context( + egl_display, + first_config, + std::ptr::null(), + &[ffi::CONTEXT_MAJOR_VERSION, 3, ffi::NONE], + )?; + + return Ok(Self { + egl, + display: egl_display, + context, + surface, + }); + }) + } + + pub fn create( + display: Option, + window: ffi::EGLNativeWindowType, + ) -> Fallible { + Self::with_egl_lib(|egl| { + let egl_display = egl.get_display(display)?; + + let (major, minor) = egl.initialize_and_get_version(egl_display)?; + log::error!("initialized EGL version {}.{}", major, minor); + + let configs = egl.choose_config( + egl_display, + &[ + ffi::ALPHA_SIZE, + 8, + ffi::RED_SIZE, + 8, + ffi::GREEN_SIZE, + 8, + ffi::BLUE_SIZE, + 8, + ffi::DEPTH_SIZE, + 24, + ffi::CONFORMANT, + ffi::OPENGL_ES3_BIT, + ffi::RENDERABLE_TYPE, + ffi::OPENGL_ES3_BIT, + ffi::SURFACE_TYPE, + ffi::WINDOW_BIT | ffi::PBUFFER_BIT | ffi::PIXMAP_BIT, + ffi::NONE, + ], + )?; + + let first_config = *configs + .first() + .ok_or_else(|| failure::err_msg("no compatible EGL configuration was found"))?; + + let surface = egl.create_window_surface(egl_display, first_config, window)?; + + let context = egl.create_context( + egl_display, + first_config, + std::ptr::null(), + &[ffi::CONTEXT_MAJOR_VERSION, 3, ffi::NONE], + )?; + + return Ok(Self { + egl, + display: egl_display, + context, + surface, + }); + }) + } } unsafe impl glium::backend::Backend for GlState { diff --git a/window/src/os/wayland/connection.rs b/window/src/os/wayland/connection.rs index 8b7724175..35e69dc09 100644 --- a/window/src/os/wayland/connection.rs +++ b/window/src/os/wayland/connection.rs @@ -19,7 +19,7 @@ use toolkit::reexports::client::{Display, EventQueue}; use toolkit::Environment; pub struct WaylandConnection { - display: RefCell, + pub(crate) display: RefCell, event_q: RefCell, pub(crate) environment: RefCell, should_terminate: RefCell, diff --git a/window/src/os/wayland/window.rs b/window/src/os/wayland/window.rs index 48bd3fc91..457da638b 100644 --- a/window/src/os/wayland/window.rs +++ b/window/src/os/wayland/window.rs @@ -36,6 +36,7 @@ use toolkit::reexports::client::protocol::wl_seat::{Event as SeatEvent, WlSeat}; use toolkit::reexports::client::protocol::wl_surface::WlSurface; use toolkit::utils::MemPool; use toolkit::window::Event; +use wayland_client::egl::{is_available as egl_is_available, WlEglSurface}; fn modifier_keys(state: ModifiersState) -> Modifiers { let mut mods = Modifiers::NONE; @@ -294,6 +295,13 @@ pub struct WaylandWindowInner { last_mouse_coords: Point, mouse_buttons: MouseButtons, modifiers: Modifiers, + // wegl_surface is listed before gl_state because it + // must be dropped before gl_state otherwise the underlying + // libraries will segfault on shutdown + #[cfg(feature = "opengl")] + wegl_surface: Option, + #[cfg(feature = "opengl")] + gl_state: Option>, } #[derive(Clone, Debug)] @@ -504,6 +512,10 @@ impl WaylandWindow { last_mouse_coords: Point::new(0, 0), mouse_buttons: MouseButtons::NONE, modifiers: Modifiers::NONE, + #[cfg(feature = "opengl")] + gl_state: None, + #[cfg(feature = "opengl")] + wegl_surface: None, })); let window_handle = Window::Wayland(WaylandWindow(window_id)); @@ -681,6 +693,12 @@ impl WaylandWindowInner { let w = w * factor as u32; let h = h * factor as u32; self.dimensions = (w, h); + #[cfg(feature = "opengl")] + { + if let Some(wegl_surface) = self.wegl_surface.as_mut() { + wegl_surface.resize(w as i32, h as i32, 0, 0); + } + } self.callbacks.resize(Dimensions { pixel_width: w as usize, pixel_height: h as usize, @@ -700,6 +718,24 @@ impl WaylandWindowInner { } fn do_paint(&mut self) -> Fallible<()> { + #[cfg(feature = "opengl")] + { + if let Some(gl_context) = self.gl_state.as_ref() { + let mut frame = glium::Frame::new( + Rc::clone(&gl_context), + (u32::from(self.dimensions.0), u32::from(self.dimensions.1)), + ); + + self.callbacks.paint_opengl(&mut frame); + frame.finish()?; + // self.damage(); + self.surface.commit(); + self.refresh_frame(); + self.need_paint = false; + return Ok(()); + } + } + if self.pool.is_used() { // Buffer still in use by server; retry later return Ok(()); @@ -901,12 +937,23 @@ impl WindowOps for WaylandWindow { { WaylandConnection::with_window_inner(self.0, move |inner| { let window = Window::Wayland(WaylandWindow(inner.window_id)); + let wayland_conn = Connection::get().unwrap().wayland(); + let mut wegl_surface = None; - /* - let gl_state = crate::egl::GlState::create( - Some(inner.conn.display as *const _), - inner.window_id as *mut _, - ) + let gl_state = if !egl_is_available() { + Err(failure::err_msg("!egl_is_available")) + } else { + wegl_surface = Some(WlEglSurface::new( + &inner.surface, + inner.dimensions.0 as i32, + inner.dimensions.1 as i32, + )); + + crate::egl::GlState::create_wayland( + Some(wayland_conn.display.borrow().get_display_ptr() as *const _), + wegl_surface.as_ref().unwrap(), + ) + } .map(Rc::new) .and_then(|state| unsafe { Ok(glium::backend::Context::new( @@ -921,14 +968,9 @@ impl WindowOps for WaylandWindow { }); inner.gl_state = gl_state.as_ref().map(Rc::clone).ok(); + inner.wegl_surface = wegl_surface; func(inner.callbacks.as_any(), &window, gl_state) - */ - func( - inner.callbacks.as_any(), - &window, - Err(failure::err_msg("no opengl")), - ) }) }