Merge pull request #303 from Smithay/feature/import_egl_buffer_reader

Move `EGLBufferReader` management into `ImportEgl`
This commit is contained in:
Victor Brekenfeld 2021-06-24 22:54:58 +02:00 committed by GitHub
commit 736e4e8bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 236 additions and 267 deletions

View File

@ -3,11 +3,9 @@
use std::cell::RefCell; use std::cell::RefCell;
use slog::Logger; use slog::Logger;
#[cfg(feature = "egl")]
use smithay::backend::{egl::display::EGLBufferReader, renderer::ImportEgl};
use smithay::{ use smithay::{
backend::{ backend::{
renderer::{buffer_type, BufferType, Frame, ImportDma, ImportShm, Renderer, Texture, Transform}, renderer::{buffer_type, BufferType, Frame, ImportAll, Renderer, Texture, Transform},
SwapBuffersError, SwapBuffersError,
}, },
reexports::wayland_server::protocol::{wl_buffer, wl_surface}, reexports::wayland_server::protocol::{wl_buffer, wl_surface},
@ -18,11 +16,6 @@ use smithay::{
seat::CursorImageRole, seat::CursorImageRole,
}, },
}; };
// hacky...
#[cfg(not(feature = "egl"))]
pub trait ImportEgl {}
#[cfg(not(feature = "egl"))]
impl<T> ImportEgl for T {}
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
@ -43,13 +36,12 @@ pub fn draw_cursor<R, E, F, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F, frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
log: &Logger, log: &Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma, R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>, F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>, E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static, T: Texture + 'static,
@ -64,29 +56,19 @@ where
(0, 0) (0, 0)
} }
}; };
draw_surface_tree( draw_surface_tree(renderer, frame, surface, (x - dx, y - dy), token, log)
renderer,
frame,
surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
(x - dx, y - dy),
token,
log,
)
} }
fn draw_surface_tree<R, E, F, T>( fn draw_surface_tree<R, E, F, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F, frame: &mut F,
root: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
location: (i32, i32), location: (i32, i32),
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
log: &Logger, log: &Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma, R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>, F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>, E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static, T: Texture + 'static,
@ -102,54 +84,36 @@ where
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
if data.texture.is_none() { if data.texture.is_none() {
if let Some(buffer) = data.current_state.buffer.take() { if let Some(buffer) = data.current_state.buffer.take() {
let texture = match buffer_type( let damage = attributes
&buffer, .damage
#[cfg(feature = "egl")] .iter()
egl_buffer_reader, .map(|dmg| match dmg {
) { Damage::Buffer(rect) => *rect,
Some(BufferType::Shm) => { // TODO also apply transformations
let damage = attributes Damage::Surface(rect) => rect.scale(attributes.buffer_scale),
.damage })
.iter() .collect::<Vec<_>>();
.map(|dmg| match dmg {
Damage::Buffer(rect) => *rect, match renderer.import_buffer(&buffer, Some(&attributes), &damage) {
// TODO also apply transformations Some(Ok(m)) => {
Damage::Surface(rect) => rect.scale(attributes.buffer_scale), if let Some(BufferType::Shm) = buffer_type(&buffer) {
})
.collect::<Vec<_>>();
let result = renderer.import_shm_buffer(&buffer, Some(&attributes), &damage);
buffer.release();
// don't return the buffer as it is already released
Some((result, None))
}
#[cfg(feature = "egl")]
Some(BufferType::Egl) => Some((
renderer.import_egl_buffer(&buffer, egl_buffer_reader.unwrap()),
Some(buffer),
)),
Some(BufferType::Dma) => {
Some((renderer.import_dma_buffer(&buffer), Some(buffer)))
}
_ => {
error!(log, "Unknown buffer format for: {:?}", buffer);
buffer.release();
None
}
};
match texture {
Some((Ok(m), buffer)) => {
data.texture = Some(Box::new(BufferTextures { buffer, texture: m })
as Box<dyn std::any::Any + 'static>)
}
// there was an error reading the buffer, release it.
Some((Err(err), buffer)) => {
warn!(log, "Error loading buffer: {:?}", err);
if let Some(buffer) = buffer {
buffer.release(); buffer.release();
} }
data.texture = Some(Box::new(BufferTextures {
buffer: Some(buffer),
texture: m,
})
as Box<dyn std::any::Any + 'static>)
} }
None => {} Some(Err(err)) => {
}; warn!(log, "Error loading buffer: {:?}", err);
buffer.release();
}
None => {
error!(log, "Unknown buffer format for: {:?}", buffer);
buffer.release();
}
}
} }
} }
// Now, should we be drawn ? // Now, should we be drawn ?
@ -204,14 +168,13 @@ where
pub fn draw_windows<R, E, F, T>( pub fn draw_windows<R, E, F, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F, frame: &mut F,
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
window_map: &MyWindowMap, window_map: &MyWindowMap,
output_rect: Option<Rectangle>, output_rect: Option<Rectangle>,
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
log: &::slog::Logger, log: &::slog::Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma, R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>, F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>, E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static, T: Texture + 'static,
@ -229,16 +192,9 @@ where
} }
if let Some(wl_surface) = toplevel_surface.get_surface() { if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
if let Err(err) = draw_surface_tree( if let Err(err) =
renderer, draw_surface_tree(renderer, frame, &wl_surface, initial_place, compositor_token, log)
frame, {
&wl_surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
initial_place,
compositor_token,
log,
) {
result = Err(err); result = Err(err);
} }
} }
@ -251,13 +207,12 @@ pub fn draw_dnd_icon<R, E, F, T>(
renderer: &mut R, renderer: &mut R,
frame: &mut F, frame: &mut F,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
log: &::slog::Logger, log: &::slog::Logger,
) -> Result<(), SwapBuffersError> ) -> Result<(), SwapBuffersError>
where where
R: Renderer<Error = E, TextureId = T, Frame = F> + ImportShm + ImportEgl + ImportDma, R: Renderer<Error = E, TextureId = T, Frame = F> + ImportAll,
F: Frame<Error = E, TextureId = T>, F: Frame<Error = E, TextureId = T>,
E: std::error::Error + Into<SwapBuffersError>, E: std::error::Error + Into<SwapBuffersError>,
T: Texture + 'static, T: Texture + 'static,
@ -268,14 +223,5 @@ where
"Trying to display as a dnd icon a surface that does not have the DndIcon role." "Trying to display as a dnd icon a surface that does not have the DndIcon role."
); );
} }
draw_surface_tree( draw_surface_tree(renderer, frame, surface, (x, y), token, log)
renderer,
frame,
surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
(x, y),
token,
log,
)
} }

View File

@ -4,8 +4,6 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
#[cfg(feature = "egl")]
use smithay::backend::egl::display::EGLBufferReader;
use smithay::{ use smithay::{
backend::renderer::buffer_dimensions, backend::renderer::buffer_dimensions,
reexports::{ reexports::{
@ -324,22 +322,15 @@ pub struct ShellHandles {
pub window_map: Rc<RefCell<MyWindowMap>>, pub window_map: Rc<RefCell<MyWindowMap>>,
} }
pub fn init_shell<Backend: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles { pub fn init_shell<BackendData: 'static>(display: &mut Display, log: ::slog::Logger) -> ShellHandles {
// Create the compositor // Create the compositor
let (compositor_token, _, _) = compositor_init( let (compositor_token, _, _) = compositor_init(
display, display,
move |request, surface, ctoken, mut ddata| match request { move |request, surface, ctoken, mut ddata| match request {
SurfaceEvent::Commit => { SurfaceEvent::Commit => {
let anvil_state = ddata.get::<AnvilState<Backend>>().unwrap(); let anvil_state = ddata.get::<AnvilState<BackendData>>().unwrap();
let window_map = anvil_state.window_map.as_ref(); let window_map = anvil_state.window_map.as_ref();
#[cfg(feature = "egl")] surface_commit(&surface, ctoken, &*window_map)
{
surface_commit(&surface, ctoken, anvil_state.egl_reader.as_ref(), &*window_map)
}
#[cfg(not(feature = "egl"))]
{
surface_commit(&surface, ctoken, &*window_map)
}
} }
}, },
log.clone(), log.clone(),
@ -850,7 +841,6 @@ impl SurfaceData {
fn surface_commit( fn surface_commit(
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
token: CompositorToken<Roles>, token: CompositorToken<Roles>,
#[cfg(feature = "egl")] egl_reader: Option<&EGLBufferReader>,
window_map: &RefCell<MyWindowMap>, window_map: &RefCell<MyWindowMap>,
) { ) {
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
@ -924,14 +914,7 @@ fn surface_commit(
match attributes.buffer.take() { match attributes.buffer.take() {
Some(BufferAssignment::NewBuffer { buffer, .. }) => { Some(BufferAssignment::NewBuffer { buffer, .. }) => {
// new contents // new contents
#[cfg(feature = "egl")] next_state.dimensions = buffer_dimensions(&buffer);
{
next_state.dimensions = buffer_dimensions(&buffer, egl_reader);
}
#[cfg(not(feature = "egl"))]
{
next_state.dimensions = buffer_dimensions(&buffer);
}
next_state.buffer = Some(buffer); next_state.buffer = Some(buffer);
} }
Some(BufferAssignment::Removed) => { Some(BufferAssignment::Removed) => {

View File

@ -20,8 +20,6 @@ use smithay::{
}, },
}; };
#[cfg(feature = "egl")]
use smithay::backend::egl::display::EGLBufferReader;
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
use smithay::xwayland::{XWayland, XWaylandEvent}; use smithay::xwayland::{XWayland, XWaylandEvent};
@ -45,8 +43,7 @@ pub struct AnvilState<BackendData> {
pub cursor_status: Arc<Mutex<CursorImageStatus>>, pub cursor_status: Arc<Mutex<CursorImageStatus>>,
pub seat_name: String, pub seat_name: String,
pub start_time: std::time::Instant, pub start_time: std::time::Instant,
#[cfg(feature = "egl")] // things we must keep alive
pub egl_reader: Option<EGLBufferReader>,
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
pub xwayland: XWayland<AnvilState<BackendData>>, pub xwayland: XWayland<AnvilState<BackendData>>,
} }
@ -56,7 +53,6 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
display: Rc<RefCell<Display>>, display: Rc<RefCell<Display>>,
handle: LoopHandle<'static, AnvilState<BackendData>>, handle: LoopHandle<'static, AnvilState<BackendData>>,
backend_data: BackendData, backend_data: BackendData,
#[cfg(feature = "egl")] egl_reader: Option<EGLBufferReader>,
log: slog::Logger, log: slog::Logger,
) -> AnvilState<BackendData> { ) -> AnvilState<BackendData> {
// init the wayland connection // init the wayland connection
@ -170,8 +166,6 @@ impl<BackendData: Backend + 'static> AnvilState<BackendData> {
cursor_status, cursor_status,
pointer_location: (0.0, 0.0), pointer_location: (0.0, 0.0),
seat_name, seat_name,
#[cfg(feature = "egl")]
egl_reader,
start_time: std::time::Instant::now(), start_time: std::time::Instant::now(),
#[cfg(feature = "xwayland")] #[cfg(feature = "xwayland")]
xwayland, xwayland,

View File

@ -58,7 +58,11 @@ use smithay::{
}; };
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use smithay::{ use smithay::{
backend::{drm::DevPath, egl::display::EGLBufferReader, renderer::ImportDma, udev::primary_gpu}, backend::{
drm::DevPath,
renderer::{ImportDma, ImportEgl},
udev::primary_gpu,
},
wayland::dmabuf::init_dmabuf_global, wayland::dmabuf::init_dmabuf_global,
}; };
@ -130,14 +134,7 @@ pub fn run_udev(
pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(), pointer_image: ImageBuffer::from_raw(64, 64, pointer_bytes.to_vec()).unwrap(),
render_timer: timer.handle(), render_timer: timer.handle(),
}; };
let mut state = AnvilState::init( let mut state = AnvilState::init(display.clone(), event_loop.handle(), data, log.clone());
display.clone(),
event_loop.handle(),
data,
#[cfg(feature = "egl")]
None,
log.clone(),
);
// re-render timer // re-render timer
event_loop event_loop
@ -460,17 +457,6 @@ impl AnvilState<UdevData> {
} }
}; };
#[cfg(feature = "egl")]
let is_primary = path.canonicalize().ok() == self.backend_data.primary_gpu;
// init hardware acceleration on the primary gpu.
#[cfg(feature = "egl")]
{
if is_primary {
info!(self.log, "Initializing EGL Hardware Acceleration via {:?}", path);
self.egl_reader = egl.bind_wl_display(&*self.display.borrow()).ok();
}
}
let context = match EGLContext::new(&egl, self.log.clone()) { let context = match EGLContext::new(&egl, self.log.clone()) {
Ok(context) => context, Ok(context) => context,
Err(err) => { Err(err) => {
@ -486,6 +472,15 @@ impl AnvilState<UdevData> {
Gles2Renderer::new(context, self.log.clone()).unwrap() Gles2Renderer::new(context, self.log.clone()).unwrap()
})); }));
#[cfg(feature = "egl")]
if path.canonicalize().ok() == self.backend_data.primary_gpu {
info!(self.log, "Initializing EGL Hardware Acceleration via {:?}", path);
renderer
.borrow_mut()
.bind_wl_display(&*self.display.borrow())
.expect("Unable to bind Wl Display?");
}
let backends = Rc::new(RefCell::new(scan_connectors( let backends = Rc::new(RefCell::new(scan_connectors(
&mut device, &mut device,
&gbm, &gbm,
@ -597,12 +592,8 @@ impl AnvilState<UdevData> {
// don't use hardware acceleration anymore, if this was the primary gpu // don't use hardware acceleration anymore, if this was the primary gpu
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
{ if _device.dev_path().and_then(|path| path.canonicalize().ok()) == self.backend_data.primary_gpu {
if _device.dev_path().and_then(|path| path.canonicalize().ok()) backend_data.renderer.borrow_mut().unbind_wl_display();
== self.backend_data.primary_gpu
{
self.egl_reader = None;
}
} }
debug!(self.log, "Dropping device"); debug!(self.log, "Dropping device");
} }
@ -637,8 +628,6 @@ impl AnvilState<UdevData> {
let result = render_surface( let result = render_surface(
&mut *surface.borrow_mut(), &mut *surface.borrow_mut(),
&mut *device_backend.renderer.borrow_mut(), &mut *device_backend.renderer.borrow_mut(),
#[cfg(feature = "egl")]
self.egl_reader.as_ref(),
device_backend.dev_id, device_backend.dev_id,
crtc, crtc,
&mut *self.window_map.borrow_mut(), &mut *self.window_map.borrow_mut(),
@ -687,7 +676,6 @@ impl AnvilState<UdevData> {
fn render_surface( fn render_surface(
surface: &mut RenderSurface, surface: &mut RenderSurface,
renderer: &mut Gles2Renderer, renderer: &mut Gles2Renderer,
#[cfg(feature = "egl")] egl_buffer_reader: Option<&EGLBufferReader>,
device_id: dev_t, device_id: dev_t,
crtc: crtc::Handle, crtc: crtc::Handle,
window_map: &mut MyWindowMap, window_map: &mut MyWindowMap,
@ -726,8 +714,6 @@ fn render_surface(
draw_windows( draw_windows(
renderer, renderer,
frame, frame,
#[cfg(feature = "egl")]
egl_buffer_reader,
window_map, window_map,
Some(Rectangle { Some(Rectangle {
x: x as i32, x: x as i32,
@ -754,8 +740,6 @@ fn render_surface(
renderer, renderer,
frame, frame,
wl_surface, wl_surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
(ptr_x, ptr_y), (ptr_x, ptr_y),
*compositor_token, *compositor_token,
logger, logger,
@ -779,8 +763,6 @@ fn render_surface(
renderer, renderer,
frame, frame,
wl_surface, wl_surface,
#[cfg(feature = "egl")]
egl_buffer_reader,
(ptr_x, ptr_y), (ptr_x, ptr_y),
*compositor_token, *compositor_token,
logger, logger,

View File

@ -38,9 +38,7 @@ pub fn run_winit(
let renderer = Rc::new(RefCell::new(renderer)); let renderer = Rc::new(RefCell::new(renderer));
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let reader = renderer.borrow().bind_wl_display(&display.borrow()).ok(); if renderer.borrow().bind_wl_display(&display.borrow()).is_ok() {
#[cfg(feature = "egl")]
if reader.is_some() {
info!(log, "EGL hardware-acceleration enabled"); info!(log, "EGL hardware-acceleration enabled");
let dmabuf_formats = renderer let dmabuf_formats = renderer
.borrow_mut() .borrow_mut()
@ -67,8 +65,6 @@ pub fn run_winit(
display.clone(), display.clone(),
event_loop.handle(), event_loop.handle(),
WinitData, WinitData,
#[cfg(feature = "egl")]
reader,
log.clone(), log.clone(),
); );
@ -129,8 +125,6 @@ pub fn run_winit(
draw_windows( draw_windows(
renderer, renderer,
frame, frame,
#[cfg(feature = "egl")]
state.egl_reader.as_ref(),
&*state.window_map.borrow(), &*state.window_map.borrow(),
None, None,
state.ctoken, state.ctoken,
@ -147,8 +141,6 @@ pub fn run_winit(
renderer, renderer,
frame, frame,
surface, surface,
#[cfg(feature = "egl")]
state.egl_reader.as_ref(),
(x as i32, y as i32), (x as i32, y as i32),
state.ctoken, state.ctoken,
&log, &log,
@ -171,16 +163,7 @@ pub fn run_winit(
// draw as relevant // draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard { if let CursorImageStatus::Image(ref surface) = *guard {
cursor_visible = false; cursor_visible = false;
draw_cursor( draw_cursor(renderer, frame, surface, (x as i32, y as i32), state.ctoken, &log)?;
renderer,
frame,
surface,
#[cfg(feature = "egl")]
state.egl_reader.as_ref(),
(x as i32, y as i32),
state.ctoken,
&log,
)?;
} else { } else {
cursor_visible = true; cursor_visible = true;
} }

View File

@ -5,6 +5,8 @@ use std::ffi::CStr;
use std::mem::MaybeUninit; use std::mem::MaybeUninit;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use std::sync::{Mutex, Weak};
use libc::c_void; use libc::c_void;
use nix::libc::c_int; use nix::libc::c_int;
@ -24,6 +26,11 @@ use crate::backend::egl::{
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))] #[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use crate::backend::egl::{BufferAccessError, EGLBuffer, Format}; use crate::backend::egl::{BufferAccessError, EGLBuffer, Format};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
lazy_static! {
pub(crate) static ref BUFFER_READER: Mutex<Option<WeakBufferReader>> = Mutex::new(None);
}
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed /// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
/// once all resources bound to it have been dropped. /// once all resources bound to it have been dropped.
#[derive(Debug)] #[derive(Debug)]
@ -535,7 +542,18 @@ impl EGLDisplay {
ffi::egl::BindWaylandDisplayWL(**self.display, display.c_ptr() as *mut _) ffi::egl::BindWaylandDisplayWL(**self.display, display.c_ptr() as *mut _)
}) })
.map_err(Error::OtherEGLDisplayAlreadyBound)?; .map_err(Error::OtherEGLDisplayAlreadyBound)?;
Ok(EGLBufferReader::new(self.display.clone(), display.c_ptr())) let reader = EGLBufferReader::new(self.display.clone(), display.c_ptr());
let mut global = BUFFER_READER.lock().unwrap();
if global.as_ref().and_then(|x| x.upgrade()).is_some() {
warn!(
self.logger,
"Double bind_wl_display, smithay does not support this, please report"
);
}
*global = Some(WeakBufferReader {
display: Arc::downgrade(&self.display),
});
Ok(reader)
} }
} }
@ -663,6 +681,24 @@ pub struct EGLBufferReader {
wayland: Option<Arc<*mut wl_display>>, wayland: Option<Arc<*mut wl_display>>,
} }
#[cfg(feature = "use_system_lib")]
pub(crate) struct WeakBufferReader {
display: Weak<EGLDisplayHandle>,
}
#[cfg(feature = "use_system_lib")]
impl WeakBufferReader {
pub fn upgrade(&self) -> Option<EGLBufferReader> {
Some(EGLBufferReader {
display: self.display.upgrade()?,
wayland: None,
})
}
}
#[cfg(feature = "use_system_lib")]
unsafe impl Send for EGLBufferReader {}
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
impl EGLBufferReader { impl EGLBufferReader {
fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self { fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {
@ -825,7 +861,7 @@ impl EGLBufferReader {
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
impl Drop for EGLBufferReader { impl Drop for EGLBufferReader {
fn drop(&mut self) { fn drop(&mut self) {
if let Ok(wayland) = Arc::try_unwrap(self.wayland.take().unwrap()) { if let Some(wayland) = self.wayland.take().and_then(|x| Arc::try_unwrap(x).ok()) {
if !wayland.is_null() { if !wayland.is_null() {
unsafe { unsafe {
// ignore errors on drop // ignore errors on drop

View File

@ -152,6 +152,8 @@ pub struct Gles2Renderer {
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
dmabuf_cache: HashMap<WeakDmabuf, Gles2Texture>, dmabuf_cache: HashMap<WeakDmabuf, Gles2Texture>,
egl: EGLContext, egl: EGLContext,
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
egl_reader: Option<EGLBufferReader>,
gl: ffi::Gles2, gl: ffi::Gles2,
destruction_callback: Receiver<CleanupResource>, destruction_callback: Receiver<CleanupResource>,
destruction_callback_sender: Sender<CleanupResource>, destruction_callback_sender: Sender<CleanupResource>,
@ -446,6 +448,8 @@ impl Gles2Renderer {
id: RENDERER_COUNTER.fetch_add(1, Ordering::SeqCst), id: RENDERER_COUNTER.fetch_add(1, Ordering::SeqCst),
gl, gl,
egl: context, egl: context,
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
egl_reader: None,
extensions: exts, extensions: exts,
programs, programs,
target_buffer: None, target_buffer: None,
@ -619,15 +623,33 @@ impl ImportShm for Gles2Renderer {
feature = "use_system_lib" feature = "use_system_lib"
))] ))]
impl ImportEgl for Gles2Renderer { impl ImportEgl for Gles2Renderer {
fn import_egl_buffer( fn bind_wl_display(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, display: &wayland_server::Display,
reader: &EGLBufferReader, ) -> Result<(), crate::backend::egl::Error> {
) -> Result<Gles2Texture, Gles2Error> { self.egl_reader = Some(self.egl.display.bind_wl_display(display)?);
Ok(())
}
fn unbind_wl_display(&mut self) {
self.egl_reader = None;
}
fn egl_reader(&self) -> Option<&EGLBufferReader> {
self.egl_reader.as_ref()
}
fn import_egl_buffer(&mut self, buffer: &wl_buffer::WlBuffer) -> Result<Gles2Texture, Gles2Error> {
if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") { if !self.extensions.iter().any(|ext| ext == "GL_OES_EGL_image") {
return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"])); return Err(Gles2Error::GLExtensionNotSupported(&["GL_OES_EGL_image"]));
} }
if self.egl_reader().is_none() {
return Err(Gles2Error::EGLBufferAccessError(
crate::backend::egl::BufferAccessError::NotManaged(crate::backend::egl::EGLError::BadDisplay),
));
}
// We can not use the caching logic for textures here as the // We can not use the caching logic for textures here as the
// egl buffers a potentially managed external which will fail the // egl buffers a potentially managed external which will fail the
// clean up check if the buffer is still alive. For wl_drm the // clean up check if the buffer is still alive. For wl_drm the
@ -635,7 +657,10 @@ impl ImportEgl for Gles2Renderer {
// will never be cleaned up. // will never be cleaned up.
self.make_current()?; self.make_current()?;
let egl = reader let egl = self
.egl_reader
.as_ref()
.unwrap()
.egl_buffer_contents(&buffer) .egl_buffer_contents(&buffer)
.map_err(Gles2Error::EGLBufferAccessError)?; .map_err(Gles2Error::EGLBufferAccessError)?;
@ -905,6 +930,8 @@ impl Drop for Gles2Renderer {
let _ = Box::from_raw(logger_ptr); let _ = Box::from_raw(logger_ptr);
} }
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
let _ = self.egl_reader.take();
let _ = self.egl.unbind(); let _ = self.egl.unbind();
} }
} }

View File

@ -25,7 +25,10 @@ use crate::backend::allocator::{dmabuf::Dmabuf, Format};
feature = "backend_egl", feature = "backend_egl",
feature = "use_system_lib" feature = "use_system_lib"
))] ))]
use crate::backend::egl::display::EGLBufferReader; use crate::backend::egl::{
display::{EGLBufferReader, BUFFER_READER},
Error as EglError,
};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
/// Possible transformations to two-dimensional planes /// Possible transformations to two-dimensional planes
@ -281,6 +284,34 @@ pub trait ImportShm: Renderer {
))] ))]
/// Trait for Renderers supporting importing wl_drm-based buffers. /// Trait for Renderers supporting importing wl_drm-based buffers.
pub trait ImportEgl: Renderer { pub trait ImportEgl: Renderer {
/// Binds the underlying EGL display to the given Wayland display.
///
/// This will allow clients to utilize EGL to create hardware-accelerated
/// surfaces. This renderer will thus be able to handle wl_drm-based buffers.
///
/// ## Errors
///
/// This might return [`EglExtensionNotSupported`](Error::EglExtensionNotSupported)
/// if binding is not supported by the EGL implementation.
///
/// This might return [`OtherEGLDisplayAlreadyBound`](Error::OtherEGLDisplayAlreadyBound)
/// if called for the same [`Display`] multiple times, as only one egl display may be bound at any given time.
fn bind_wl_display(&mut self, display: &wayland_server::Display) -> Result<(), EglError>;
/// Unbinds a previously bound egl display, if existing.
///
/// *Note*: As a result any previously created egl-based WlBuffers will not be readable anymore.
/// Your compositor will have to deal with existing buffers of *unknown* type.
fn unbind_wl_display(&mut self);
/// Returns the underlying [`EGLBufferReader`].
///
/// The primary use for this is calling [`buffer_dimensions`] or [`buffer_type`].
///
/// Returns `None` if no [`Display`] was previously bound to the underlying [`EGLDisplay`]
/// (see [`ImportEgl::bind_wl_display`]).
fn egl_reader(&self) -> Option<&EGLBufferReader>;
/// Import a given wl_drm-based buffer into the renderer (see [`buffer_type`]). /// Import a given wl_drm-based buffer into the renderer (see [`buffer_type`]).
/// ///
/// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`])
@ -295,7 +326,6 @@ pub trait ImportEgl: Renderer {
fn import_egl_buffer( fn import_egl_buffer(
&mut self, &mut self,
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
egl: &EGLBufferReader,
) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>; ) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>;
} }
@ -351,12 +381,8 @@ pub trait ImportDma: Renderer {
// pub type ImportAll = Renderer + ImportShm + ImportEgl; // pub type ImportAll = Renderer + ImportShm + ImportEgl;
/// Common trait for renderers of any wayland buffer type /// Common trait for renderers of any wayland buffer type
#[cfg(all( #[cfg(feature = "wayland_frontend")]
feature = "wayland_frontend", pub trait ImportAll: Renderer {
feature = "backend_egl",
feature = "use_system_lib"
))]
pub trait ImportAll: Renderer + ImportShm + ImportEgl {
/// Import a given buffer into the renderer. /// Import a given buffer into the renderer.
/// ///
/// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`]) /// Returns a texture_id, which can be used with [`Frame::render_texture`] (or [`Frame::render_texture_at`])
@ -380,21 +406,49 @@ pub trait ImportAll: Renderer + ImportShm + ImportEgl {
buffer: &wl_buffer::WlBuffer, buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>, surface: Option<&SurfaceAttributes>,
damage: &[Rectangle], damage: &[Rectangle],
egl: Option<&EGLBufferReader>, ) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>>;
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
match buffer_type(buffer, egl) {
Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
Some(BufferType::Egl) => Some(self.import_egl_buffer(buffer, egl.unwrap())),
_ => None,
}
}
} }
// TODO: Do this with specialization, when possible and do default implementations
#[cfg(all( #[cfg(all(
feature = "wayland_frontend", feature = "wayland_frontend",
feature = "backend_egl", feature = "backend_egl",
feature = "use_system_lib" feature = "use_system_lib"
))] ))]
impl<R: Renderer + ImportShm + ImportEgl> ImportAll for R {} impl<R: Renderer + ImportShm + ImportEgl + ImportDma> ImportAll for R {
fn import_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
damage: &[Rectangle],
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
match buffer_type(buffer) {
Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
Some(BufferType::Egl) => Some(self.import_egl_buffer(buffer)),
Some(BufferType::Dma) => Some(self.import_dma_buffer(buffer)),
_ => None,
}
}
}
#[cfg(all(
feature = "wayland_frontend",
not(all(feature = "backend_egl", feature = "use_system_lib"))
))]
impl<R: Renderer + ImportShm + ImportDma> ImportAll for R {
fn import_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&SurfaceAttributes>,
damage: &[Rectangle],
) -> Option<Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error>> {
match buffer_type(buffer) {
Some(BufferType::Shm) => Some(self.import_shm_buffer(buffer, surface, damage)),
Some(BufferType::Dma) => Some(self.import_dma_buffer(buffer)),
_ => None,
}
}
}
#[cfg(feature = "wayland_frontend")] #[cfg(feature = "wayland_frontend")]
#[non_exhaustive] #[non_exhaustive]
@ -413,94 +467,58 @@ pub enum BufferType {
/// ///
/// Returns `None` if the type is not known to smithay /// Returns `None` if the type is not known to smithay
/// or otherwise not supported (e.g. not initialized using one of smithays [`crate::wayland`]-handlers). /// or otherwise not supported (e.g. not initialized using one of smithays [`crate::wayland`]-handlers).
#[cfg(all( #[cfg(feature = "wayland_frontend")]
feature = "wayland_frontend", pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option<BufferType> {
feature = "backend_egl",
feature = "use_system_lib"
))]
pub fn buffer_type(
buffer: &wl_buffer::WlBuffer,
egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<BufferType> {
if buffer.as_ref().user_data().get::<Dmabuf>().is_some() { if buffer.as_ref().user_data().get::<Dmabuf>().is_some() {
Some(BufferType::Dma) return Some(BufferType::Dma);
} else if egl_buffer_reader }
#[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
if BUFFER_READER
.lock()
.unwrap()
.as_ref() .as_ref()
.and_then(|x| x.upgrade())
.and_then(|x| x.egl_buffer_dimensions(&buffer)) .and_then(|x| x.egl_buffer_dimensions(&buffer))
.is_some() .is_some()
{ {
Some(BufferType::Egl) return Some(BufferType::Egl);
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
Some(BufferType::Shm)
} else {
None
} }
}
/// Returns the *type* of a wl_buffer if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
/// return Some(BufferType::Shm);
/// Returns `None` if the type is not recognized by smithay or otherwise not supported.
#[cfg(all(
feature = "wayland_frontend",
not(all(feature = "backend_egl", feature = "use_system_lib"))
))]
pub fn buffer_type(buffer: &wl_buffer::WlBuffer) -> Option<BufferType> {
if buffer.as_ref().user_data().get::<Dmabuf>().is_some() {
Some(BufferType::Dma)
} else if crate::wayland::shm::with_buffer_contents(&buffer, |_, _| ()).is_ok() {
Some(BufferType::Shm)
} else {
None
} }
None
} }
/// Returns the dimensions of a wl_buffer /// Returns the dimensions of a wl_buffer
/// ///
/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`]) /// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
#[cfg(all( #[cfg(feature = "wayland_frontend")]
feature = "wayland_frontend",
feature = "backend_egl",
feature = "use_system_lib"
))]
pub fn buffer_dimensions(
buffer: &wl_buffer::WlBuffer,
egl_buffer_reader: Option<&EGLBufferReader>,
) -> Option<(i32, i32)> {
use crate::backend::allocator::Buffer;
if let Some(buf) = buffer.as_ref().user_data().get::<Dmabuf>() {
Some((buf.width() as i32, buf.height() as i32))
} else if let Some((w, h)) = egl_buffer_reader
.as_ref()
.and_then(|x| x.egl_buffer_dimensions(&buffer))
{
Some((w, h))
} else if let Ok((w, h)) =
crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
{
Some((w, h))
} else {
None
}
}
/// Returns the dimensions of a wl_buffer
///
/// *Note*: This will only return dimensions for buffer types known to smithay (see [`buffer_type`])
#[cfg(all(
feature = "wayland_frontend",
not(all(feature = "backend_egl", feature = "use_system_lib"))
))]
pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> { pub fn buffer_dimensions(buffer: &wl_buffer::WlBuffer) -> Option<(i32, i32)> {
use crate::backend::allocator::Buffer; use crate::backend::allocator::Buffer;
if let Some(buf) = buffer.as_ref().user_data().get::<Dmabuf>() { if let Some(buf) = buffer.as_ref().user_data().get::<Dmabuf>() {
Some((buf.width() as i32, buf.height() as i32)) return Some((buf.width() as i32, buf.height() as i32));
} else if let Ok((w, h)) = }
#[cfg(all(feature = "backend_egl", feature = "use_system_lib"))]
if let Some((w, h)) = BUFFER_READER
.lock()
.unwrap()
.as_ref()
.and_then(|x| x.upgrade())
.and_then(|x| x.egl_buffer_dimensions(&buffer))
{
return Some((w, h));
}
if let Ok((w, h)) =
crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height)) crate::wayland::shm::with_buffer_contents(&buffer, |_, data| (data.width, data.height))
{ {
Some((w, h)) return Some((w, h));
} else {
None
} }
None
} }