From 30a869378923998177ec6b6f8004e96492b541ec Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Mon, 26 Apr 2021 21:44:40 +0200 Subject: [PATCH] anvil: Reintroduce egl buffer support --- anvil/src/buffer_utils.rs | 103 +++++++++++++++++++++++++++++++++++++- anvil/src/drawing.rs | 30 +++++++---- anvil/src/udev.rs | 44 ++++++++-------- anvil/src/winit.rs | 11 ++-- 4 files changed, 149 insertions(+), 39 deletions(-) diff --git a/anvil/src/buffer_utils.rs b/anvil/src/buffer_utils.rs index 64ffb12..8cc3904 100644 --- a/anvil/src/buffer_utils.rs +++ b/anvil/src/buffer_utils.rs @@ -1,9 +1,16 @@ #[cfg(feature = "egl")] use std::{cell::RefCell, rc::Rc}; +#[cfg(feature = "udev")] +use smithay::backend::renderer::{Renderer, Texture}; +#[cfg(feature = "udev")] +use smithay::reexports::nix::libc::dev_t; +#[cfg(feature = "udev")] +use std::collections::HashMap; + #[cfg(feature = "egl")] use smithay::backend::egl::{ - display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format, + display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLBuffer, }; use smithay::{ reexports::wayland_server::protocol::wl_buffer::WlBuffer, @@ -58,4 +65,98 @@ impl BufferUtils { err }) } + + #[cfg(feature = "egl")] + pub fn load_buffer(&self, buffer: WlBuffer) -> Result, WlBuffer> { + let result = if let Some(reader) = &self.egl_buffer_reader.borrow().as_ref() { + reader.egl_buffer_contents(&buffer) + } else { + return Err(buffer); + }; + + let egl_buffer = match result { + Ok(egl) => Some(egl), + Err(EGLBufferAccessError::NotManaged(_)) => { None }, + Err(err) => { + error!(self.log, "EGL error"; "err" => format!("{:?}", err)); + return Err(buffer); + } + }; + + Ok(BufferTextures { + buffer, + textures: HashMap::new(), + egl: egl_buffer, // I guess we need to keep this alive ? + }) + } + + #[cfg(not(feature = "egl"))] + pub fn load_buffer(&self, buffer: WlBuffer) -> Result, WlBuffer> { + Ok(BufferTextures { + buffer, + textures: HashMap::new(), + }) + } } + +#[cfg(feature = "udev")] +pub struct BufferTextures { + buffer: WlBuffer, + pub textures: HashMap, + #[cfg(feature = "egl")] + egl: Option, +} + +#[cfg(feature = "udev")] +impl BufferTextures { + #[cfg(feature = "egl")] + pub fn load_texture<'a, R: Renderer>( + &'a mut self, + id: u64, + renderer: &mut R, + ) -> Result<&'a T, R::Error> { + if self.textures.contains_key(&id) { + return Ok(&self.textures[&id]); + } + + if let Some(buffer) = self.egl.as_ref() { + //EGL buffer + let texture = renderer.import_egl(&buffer)?; + self.textures.insert(id, texture); + Ok(&self.textures[&id]) + } else { + self.load_shm_texture(id, renderer) + } + } + + #[cfg(not(feature = "egl"))] + pub fn load_texture<'a, R: Renderer>( + &'a mut self, + id: u64, + renderer: &mut R, + ) -> Result<&'a T, R::Error> { + if self.textures.contains_key(&id) { + return Ok(&self.textures[&id]); + } + + self.load_shm_texture(id, renderer) + } + + fn load_shm_texture<'a, R: Renderer>( + &'a mut self, + id: u64, + renderer: &mut R, + ) -> Result<&'a T, R::Error> { + let texture = renderer.import_shm(&self.buffer)?; + + self.textures.insert(id, texture); + Ok(&self.textures[&id]) + } +} + +#[cfg(feature = "udev")] +impl Drop for BufferTextures { + fn drop(&mut self) { + self.buffer.release() + } +} \ No newline at end of file diff --git a/anvil/src/drawing.rs b/anvil/src/drawing.rs index e2250ee..fe13beb 100644 --- a/anvil/src/drawing.rs +++ b/anvil/src/drawing.rs @@ -20,10 +20,12 @@ use smithay::{ }; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; - +use crate::buffer_utils::{BufferUtils, BufferTextures}; pub fn draw_cursor( renderer: &mut R, + renderer_id: u64, + buffer_utils: &BufferUtils, surface: &wl_surface::WlSurface, (x, y): (i32, i32), token: MyCompositorToken, @@ -44,11 +46,13 @@ pub fn draw_cursor( (0, 0) } }; - draw_surface_tree(renderer, surface, (x - dx, y - dy), token, log); + draw_surface_tree(renderer, renderer_id, buffer_utils, surface, (x - dx, y - dy), token, log); } fn draw_surface_tree( renderer: &mut R, + renderer_id: u64, + buffer_utils: &BufferUtils, root: &wl_surface::WlSurface, location: (i32, i32), compositor_token: MyCompositorToken, @@ -62,21 +66,20 @@ fn draw_surface_tree( compositor_token.with_surface_tree_upward( root, (), - |_surface, attributes, role, _| { + |_surface, attributes, _, _| { // Pull a new buffer if available if let Some(data) = attributes.user_data.get::>() { let mut data = data.borrow_mut(); if data.texture.is_none() { if let Some(buffer) = data.current_state.buffer.take() { - match renderer.import_shm(&buffer) { + match buffer_utils.load_buffer::(buffer) { Ok(m) => data.texture = Some(Box::new(m) as Box), // there was an error reading the buffer, release it, we // already logged the error Err(err) => { - warn!(log, "Error loading buffer: {}", err); + warn!(log, "Error loading buffer: {:?}", err); }, }; - buffer.release(); } } // Now, should we be drawn ? @@ -101,7 +104,7 @@ fn draw_surface_tree( |_surface, attributes, role, &(mut x, mut y)| { // Pull a new buffer if available if let Some(data) = attributes.user_data.get::>() { - let mut data = data.borrow_mut(); + let data = data.borrow(); // Now, should we be drawn ? if data.texture.is_some() { // if yes, also process the children @@ -123,14 +126,15 @@ fn draw_surface_tree( if let Some(ref data) = attributes.user_data.get::>() { let mut data = data.borrow_mut(); let (sub_x, sub_y) = data.current_state.sub_location; - if let Some(buffer_texture) = data.texture.as_ref().and_then(|x| x.downcast_ref::()) { + if let Some(buffer_textures) = data.texture.as_mut().and_then(|x| x.downcast_mut::>()) { // we need to re-extract the subsurface offset, as the previous closure // only passes it to our children if Role::::has(role) { x += sub_x; y += sub_y; } - renderer.render_texture_at(&*buffer_texture, (x, y), Transform::Normal /* TODO */, 1.0); + let texture = buffer_textures.load_texture(renderer_id, renderer).unwrap(); + renderer.render_texture_at(texture, (x, y), Transform::Normal /* TODO */, 1.0); } } }, @@ -140,6 +144,8 @@ fn draw_surface_tree( pub fn draw_windows( renderer: &mut R, + renderer_id: u64, + buffer_utils: &BufferUtils, window_map: &MyWindowMap, output_rect: Option, compositor_token: MyCompositorToken, @@ -165,6 +171,8 @@ pub fn draw_windows( // this surface is a root of a subsurface tree that needs to be drawn draw_surface_tree( renderer, + renderer_id, + buffer_utils, &wl_surface, initial_place, compositor_token, @@ -178,6 +186,8 @@ pub fn draw_windows( pub fn draw_dnd_icon( renderer: &mut R, + renderer_id: u64, + buffer_utils: &BufferUtils, surface: &wl_surface::WlSurface, (x, y): (i32, i32), token: MyCompositorToken, @@ -194,7 +204,7 @@ pub fn draw_dnd_icon( "Trying to display as a dnd icon a surface that does not have the DndIcon role." ); } - draw_surface_tree(renderer, surface, (x, y), token, log); + draw_surface_tree(renderer, renderer_id, buffer_utils, surface, (x, y), token, log); } pub fn schedule_initial_render( diff --git a/anvil/src/udev.rs b/anvil/src/udev.rs index a7a5de0..0f7b0ac 100644 --- a/anvil/src/udev.rs +++ b/anvil/src/udev.rs @@ -22,8 +22,9 @@ use smithay::{ DrmRenderSurface, DrmError, DrmRenderError, + DevPath, }, - egl::{EGLDisplay, EGLContext}, + egl::{EGLDisplay, EGLContext, display::EGLBufferReader}, renderer::{ Transform, Renderer, @@ -98,14 +99,12 @@ pub fn run_udev( info!(log, "Listening on wayland socket"; "name" => name.clone()); ::std::env::set_var("WAYLAND_DISPLAY", name); - /* #[cfg(feature = "egl")] let egl_buffer_reader = Rc::new(RefCell::new(None)); #[cfg(feature = "egl")] let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone()); #[cfg(not(feature = "egl"))] - */ let buffer_utils = BufferUtils::new(log.clone()); let output_map = Rc::new(RefCell::new(Vec::new())); @@ -285,7 +284,7 @@ impl Drop for MyOutput { } } -type RenderSurface = DrmRenderSurface, Gles2Renderer, GbmBuffer<()>>; +pub type RenderSurface = DrmRenderSurface, Gles2Renderer, GbmBuffer<()>>; struct BackendData { _restart_token: SignalToken, @@ -299,8 +298,8 @@ struct BackendData { struct UdevHandlerImpl { compositor_token: CompositorToken, buffer_utils: BufferUtils, - //#[cfg(feature = "egl")] - //egl_buffer_reader: Rc>>, + #[cfg(feature = "egl")] + egl_buffer_reader: Rc>>, session: AutoSession, backends: HashMap, display: Rc>, @@ -322,7 +321,6 @@ impl UdevHandlerImpl { gbm: &GbmDevice, egl: &EGLDisplay, context: &EGLContext, - buffer_utils: &BufferUtils, display: &mut Display, output_map: &mut Vec, logger: &::slog::Logger, @@ -457,8 +455,15 @@ impl UdevHandlerImpl { } }) { + let egl = match EGLDisplay::new(&gbm, self.logger.clone()) { + Ok(display) => display, + Err(err) => { + warn!(self.logger, "Skipping device {:?}, because of egl display error: {}", device_id, err); + return; + } + }; + // init hardware acceleration on the primary gpu. - /* #[cfg(feature = "egl")] { if path.canonicalize().ok() == self.primary_gpu { @@ -467,18 +472,10 @@ impl UdevHandlerImpl { "Initializing EGL Hardware Acceleration via {:?}", path ); *self.egl_buffer_reader.borrow_mut() = - device.bind_wl_display(&*self.display.borrow()).ok(); + egl.bind_wl_display(&*self.display.borrow()).ok(); } } - */ - let egl = match EGLDisplay::new(&gbm, self.logger.clone()) { - Ok(display) => display, - Err(err) => { - warn!(self.logger, "Skipping device {:?}, because of egl display error: {}", device_id, err); - return; - } - }; let context = match EGLContext::new(&egl, self.logger.clone()) { Ok(context) => context, Err(err) => { @@ -492,7 +489,6 @@ impl UdevHandlerImpl { &gbm, &egl, &context, - &self.buffer_utils, &mut *self.display.borrow_mut(), &mut *self.output_map.borrow_mut(), &self.logger, @@ -509,6 +505,7 @@ impl UdevHandlerImpl { // to introduce reference cycles with Rc. Be sure about your drop order let renderer = Rc::new(DrmRenderer { device_id, + buffer_utils: self.buffer_utils.clone(), compositor_token: self.compositor_token, backends: backends.clone(), window_map: self.window_map.clone(), @@ -562,7 +559,6 @@ impl UdevHandlerImpl { fn device_changed(&mut self, device: dev_t) { //quick and dirty, just re-init all backends - let buffer_utils = &self.buffer_utils; if let Some(ref mut backend_data) = self.backends.get_mut(&device) { let logger = self.logger.clone(); let loop_handle = self.loop_handle.clone(); @@ -577,7 +573,6 @@ impl UdevHandlerImpl { &backend_data.gbm, &backend_data.egl, &backend_data.context, - buffer_utils, &mut *display, &mut *output_map, &logger, @@ -649,6 +644,7 @@ impl DrmRendererSessionListener { pub struct DrmRenderer { device_id: dev_t, + buffer_utils: BufferUtils, compositor_token: CompositorToken, backends: Rc>>>>, window_map: Rc>, @@ -676,6 +672,7 @@ impl DrmRenderer { if let Some(surface) = self.backends.borrow().get(&crtc) { let result = DrmRenderer::render_surface( &mut *surface.borrow_mut(), + &self.buffer_utils, self.device_id, crtc, &mut *self.window_map.borrow_mut(), @@ -748,6 +745,7 @@ impl DrmRenderer { fn render_surface( surface: &mut RenderSurface, + buffer_utils: &BufferUtils, device_id: dev_t, crtc: crtc::Handle, window_map: &mut MyWindowMap, @@ -778,6 +776,8 @@ impl DrmRenderer { // draw the surfaces draw_windows( surface, + device_id, + buffer_utils, window_map, Some(Rectangle { x: x as i32, @@ -800,7 +800,7 @@ impl DrmRenderer { { if let Some(ref wl_surface) = dnd_icon.as_ref() { if wl_surface.as_ref().is_alive() { - draw_dnd_icon(surface, wl_surface, (ptr_x, ptr_y), compositor_token.clone(), logger); + draw_dnd_icon(surface, device_id, buffer_utils, wl_surface, (ptr_x, ptr_y), compositor_token.clone(), logger); } } } @@ -818,6 +818,8 @@ impl DrmRenderer { if let CursorImageStatus::Image(ref wl_surface) = *cursor_status { draw_cursor( surface, + device_id, + buffer_utils, wl_surface, (ptr_x, ptr_y), compositor_token.clone(), diff --git a/anvil/src/winit.rs b/anvil/src/winit.rs index 8b3d6cb..a4d6abb 100644 --- a/anvil/src/winit.rs +++ b/anvil/src/winit.rs @@ -7,7 +7,6 @@ use smithay::{ reexports::{ calloop::EventLoop, wayland_server::{protocol::wl_output, Display}, - winit::window::CursorIcon, }, wayland::{ output::{Mode, Output, PhysicalProperties}, @@ -30,7 +29,6 @@ pub fn run_winit( slog::crit!(log, "Failed to initialize Winit backend: {}", err); })?; - /* #[cfg(feature = "egl")] let egl_buffer_reader = Rc::new(RefCell::new( if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) { @@ -44,7 +42,6 @@ pub fn run_winit( #[cfg(feature = "egl")] let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone()); #[cfg(not(feature = "egl"))] - */ let buffer_utils = BufferUtils::new(log.clone()); let (w, h): (u32, u32) = renderer.window_size().physical_size.into(); @@ -56,7 +53,7 @@ pub fn run_winit( let mut state = AnvilState::init( display.clone(), event_loop.handle(), - buffer_utils, + buffer_utils.clone(), None, None, log.clone(), @@ -116,7 +113,7 @@ pub fn run_winit( renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame"); // draw the windows - draw_windows(&mut renderer, &*state.window_map.borrow(), None, state.ctoken, &log); + draw_windows(&mut renderer, 0, &buffer_utils, &*state.window_map.borrow(), None, state.ctoken, &log); let (x, y) = *state.pointer_location.borrow(); // draw the dnd icon if any @@ -124,7 +121,7 @@ pub fn run_winit( let guard = state.dnd_icon.lock().unwrap(); if let Some(ref surface) = *guard { if surface.as_ref().is_alive() { - draw_dnd_icon(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log); + draw_dnd_icon(&mut renderer, 0, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log); } } } @@ -143,7 +140,7 @@ pub fn run_winit( // draw as relevant if let CursorImageStatus::Image(ref surface) = *guard { renderer.window().set_cursor_visible(false); - draw_cursor(&mut renderer, surface, (x as i32, y as i32), state.ctoken, &log); + draw_cursor(&mut renderer, 0, &buffer_utils, surface, (x as i32, y as i32), state.ctoken, &log); } else { renderer.window().set_cursor_visible(true); }