anvil: Reintroduce egl buffer support

This commit is contained in:
Victor Brekenfeld 2021-04-26 21:44:40 +02:00
parent cb8d637d38
commit 30a8693789
4 changed files with 149 additions and 39 deletions

View File

@ -1,9 +1,16 @@
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
use std::{cell::RefCell, rc::Rc}; 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")] #[cfg(feature = "egl")]
use smithay::backend::egl::{ use smithay::backend::egl::{
display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLImages, Format, display::EGLBufferReader, BufferAccessError as EGLBufferAccessError, EGLBuffer,
}; };
use smithay::{ use smithay::{
reexports::wayland_server::protocol::wl_buffer::WlBuffer, reexports::wayland_server::protocol::wl_buffer::WlBuffer,
@ -58,4 +65,98 @@ impl BufferUtils {
err err
}) })
} }
#[cfg(feature = "egl")]
pub fn load_buffer<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, 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<T>(&self, buffer: WlBuffer) -> Result<BufferTextures<T>, WlBuffer> {
Ok(BufferTextures {
buffer,
textures: HashMap::new(),
})
}
}
#[cfg(feature = "udev")]
pub struct BufferTextures<T> {
buffer: WlBuffer,
pub textures: HashMap<dev_t, T>,
#[cfg(feature = "egl")]
egl: Option<EGLBuffer>,
}
#[cfg(feature = "udev")]
impl<T: Texture> BufferTextures<T> {
#[cfg(feature = "egl")]
pub fn load_texture<'a, R: Renderer<Texture=T>>(
&'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<Texture=T>>(
&'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<Texture=T>>(
&'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<T> Drop for BufferTextures<T> {
fn drop(&mut self) {
self.buffer.release()
}
} }

View File

@ -20,10 +20,12 @@ use smithay::{
}; };
use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData}; use crate::shell::{MyCompositorToken, MyWindowMap, SurfaceData};
use crate::buffer_utils::{BufferUtils, BufferTextures};
pub fn draw_cursor<R, E, T>( pub fn draw_cursor<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64,
buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
@ -44,11 +46,13 @@ pub fn draw_cursor<R, E, T>(
(0, 0) (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<R, E, T>( fn draw_surface_tree<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64,
buffer_utils: &BufferUtils,
root: &wl_surface::WlSurface, root: &wl_surface::WlSurface,
location: (i32, i32), location: (i32, i32),
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
@ -62,21 +66,20 @@ fn draw_surface_tree<R, E, T>(
compositor_token.with_surface_tree_upward( compositor_token.with_surface_tree_upward(
root, root,
(), (),
|_surface, attributes, role, _| { |_surface, attributes, _, _| {
// Pull a new buffer if available // Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() { if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
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() {
match renderer.import_shm(&buffer) { match buffer_utils.load_buffer::<R::Texture>(buffer) {
Ok(m) => data.texture = Some(Box::new(m) as Box<dyn std::any::Any + 'static>), Ok(m) => data.texture = Some(Box::new(m) as Box<dyn std::any::Any + 'static>),
// there was an error reading the buffer, release it, we // there was an error reading the buffer, release it, we
// already logged the error // already logged the error
Err(err) => { Err(err) => {
warn!(log, "Error loading buffer: {}", err); warn!(log, "Error loading buffer: {:?}", err);
}, },
}; };
buffer.release();
} }
} }
// Now, should we be drawn ? // Now, should we be drawn ?
@ -101,7 +104,7 @@ fn draw_surface_tree<R, E, T>(
|_surface, attributes, role, &(mut x, mut y)| { |_surface, attributes, role, &(mut x, mut y)| {
// Pull a new buffer if available // Pull a new buffer if available
if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() { if let Some(data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut(); let data = data.borrow();
// Now, should we be drawn ? // Now, should we be drawn ?
if data.texture.is_some() { if data.texture.is_some() {
// if yes, also process the children // if yes, also process the children
@ -123,14 +126,15 @@ fn draw_surface_tree<R, E, T>(
if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() { if let Some(ref data) = attributes.user_data.get::<RefCell<SurfaceData>>() {
let mut data = data.borrow_mut(); let mut data = data.borrow_mut();
let (sub_x, sub_y) = data.current_state.sub_location; 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::<T>()) { if let Some(buffer_textures) = data.texture.as_mut().and_then(|x| x.downcast_mut::<BufferTextures<T>>()) {
// we need to re-extract the subsurface offset, as the previous closure // we need to re-extract the subsurface offset, as the previous closure
// only passes it to our children // only passes it to our children
if Role::<SubsurfaceRole>::has(role) { if Role::<SubsurfaceRole>::has(role) {
x += sub_x; x += sub_x;
y += sub_y; 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<R, E, T>(
pub fn draw_windows<R, E, T>( pub fn draw_windows<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64,
buffer_utils: &BufferUtils,
window_map: &MyWindowMap, window_map: &MyWindowMap,
output_rect: Option<Rectangle>, output_rect: Option<Rectangle>,
compositor_token: MyCompositorToken, compositor_token: MyCompositorToken,
@ -165,6 +171,8 @@ pub fn draw_windows<R, E, T>(
// 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
draw_surface_tree( draw_surface_tree(
renderer, renderer,
renderer_id,
buffer_utils,
&wl_surface, &wl_surface,
initial_place, initial_place,
compositor_token, compositor_token,
@ -178,6 +186,8 @@ pub fn draw_windows<R, E, T>(
pub fn draw_dnd_icon<R, E, T>( pub fn draw_dnd_icon<R, E, T>(
renderer: &mut R, renderer: &mut R,
renderer_id: u64,
buffer_utils: &BufferUtils,
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
(x, y): (i32, i32), (x, y): (i32, i32),
token: MyCompositorToken, token: MyCompositorToken,
@ -194,7 +204,7 @@ pub fn draw_dnd_icon<R, E, T>(
"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(renderer, surface, (x, y), token, log); draw_surface_tree(renderer, renderer_id, buffer_utils, surface, (x, y), token, log);
} }
pub fn schedule_initial_render<R: Renderer + 'static, Data: 'static>( pub fn schedule_initial_render<R: Renderer + 'static, Data: 'static>(

View File

@ -22,8 +22,9 @@ use smithay::{
DrmRenderSurface, DrmRenderSurface,
DrmError, DrmError,
DrmRenderError, DrmRenderError,
DevPath,
}, },
egl::{EGLDisplay, EGLContext}, egl::{EGLDisplay, EGLContext, display::EGLBufferReader},
renderer::{ renderer::{
Transform, Transform,
Renderer, Renderer,
@ -98,14 +99,12 @@ pub fn run_udev(
info!(log, "Listening on wayland socket"; "name" => name.clone()); info!(log, "Listening on wayland socket"; "name" => name.clone());
::std::env::set_var("WAYLAND_DISPLAY", name); ::std::env::set_var("WAYLAND_DISPLAY", name);
/*
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let egl_buffer_reader = Rc::new(RefCell::new(None)); let egl_buffer_reader = Rc::new(RefCell::new(None));
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone()); let buffer_utils = BufferUtils::new(egl_buffer_reader.clone(), log.clone());
#[cfg(not(feature = "egl"))] #[cfg(not(feature = "egl"))]
*/
let buffer_utils = BufferUtils::new(log.clone()); let buffer_utils = BufferUtils::new(log.clone());
let output_map = Rc::new(RefCell::new(Vec::new())); let output_map = Rc::new(RefCell::new(Vec::new()));
@ -285,7 +284,7 @@ impl Drop for MyOutput {
} }
} }
type RenderSurface = DrmRenderSurface<SessionFd, GbmDevice<SessionFd>, Gles2Renderer, GbmBuffer<()>>; pub type RenderSurface = DrmRenderSurface<SessionFd, GbmDevice<SessionFd>, Gles2Renderer, GbmBuffer<()>>;
struct BackendData { struct BackendData {
_restart_token: SignalToken, _restart_token: SignalToken,
@ -299,8 +298,8 @@ struct BackendData {
struct UdevHandlerImpl<Data: 'static> { struct UdevHandlerImpl<Data: 'static> {
compositor_token: CompositorToken<Roles>, compositor_token: CompositorToken<Roles>,
buffer_utils: BufferUtils, buffer_utils: BufferUtils,
//#[cfg(feature = "egl")] #[cfg(feature = "egl")]
//egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>, egl_buffer_reader: Rc<RefCell<Option<EGLBufferReader>>>,
session: AutoSession, session: AutoSession,
backends: HashMap<dev_t, BackendData>, backends: HashMap<dev_t, BackendData>,
display: Rc<RefCell<Display>>, display: Rc<RefCell<Display>>,
@ -322,7 +321,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
gbm: &GbmDevice<SessionFd>, gbm: &GbmDevice<SessionFd>,
egl: &EGLDisplay, egl: &EGLDisplay,
context: &EGLContext, context: &EGLContext,
buffer_utils: &BufferUtils,
display: &mut Display, display: &mut Display,
output_map: &mut Vec<MyOutput>, output_map: &mut Vec<MyOutput>,
logger: &::slog::Logger, logger: &::slog::Logger,
@ -457,8 +455,15 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
} }
}) })
{ {
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. // init hardware acceleration on the primary gpu.
/*
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
{ {
if path.canonicalize().ok() == self.primary_gpu { if path.canonicalize().ok() == self.primary_gpu {
@ -467,18 +472,10 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
"Initializing EGL Hardware Acceleration via {:?}", path "Initializing EGL Hardware Acceleration via {:?}", path
); );
*self.egl_buffer_reader.borrow_mut() = *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()) { let context = match EGLContext::new(&egl, self.logger.clone()) {
Ok(context) => context, Ok(context) => context,
Err(err) => { Err(err) => {
@ -492,7 +489,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
&gbm, &gbm,
&egl, &egl,
&context, &context,
&self.buffer_utils,
&mut *self.display.borrow_mut(), &mut *self.display.borrow_mut(),
&mut *self.output_map.borrow_mut(), &mut *self.output_map.borrow_mut(),
&self.logger, &self.logger,
@ -509,6 +505,7 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
// to introduce reference cycles with Rc. Be sure about your drop order // to introduce reference cycles with Rc. Be sure about your drop order
let renderer = Rc::new(DrmRenderer { let renderer = Rc::new(DrmRenderer {
device_id, device_id,
buffer_utils: self.buffer_utils.clone(),
compositor_token: self.compositor_token, compositor_token: self.compositor_token,
backends: backends.clone(), backends: backends.clone(),
window_map: self.window_map.clone(), window_map: self.window_map.clone(),
@ -562,7 +559,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
fn device_changed(&mut self, device: dev_t) { fn device_changed(&mut self, device: dev_t) {
//quick and dirty, just re-init all backends //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) { if let Some(ref mut backend_data) = self.backends.get_mut(&device) {
let logger = self.logger.clone(); let logger = self.logger.clone();
let loop_handle = self.loop_handle.clone(); let loop_handle = self.loop_handle.clone();
@ -577,7 +573,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
&backend_data.gbm, &backend_data.gbm,
&backend_data.egl, &backend_data.egl,
&backend_data.context, &backend_data.context,
buffer_utils,
&mut *display, &mut *display,
&mut *output_map, &mut *output_map,
&logger, &logger,
@ -649,6 +644,7 @@ impl<Data: 'static> DrmRendererSessionListener<Data> {
pub struct DrmRenderer { pub struct DrmRenderer {
device_id: dev_t, device_id: dev_t,
buffer_utils: BufferUtils,
compositor_token: CompositorToken<Roles>, compositor_token: CompositorToken<Roles>,
backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Rc<RefCell<RenderSurface>>>>>,
window_map: Rc<RefCell<MyWindowMap>>, window_map: Rc<RefCell<MyWindowMap>>,
@ -676,6 +672,7 @@ impl DrmRenderer {
if let Some(surface) = self.backends.borrow().get(&crtc) { if let Some(surface) = self.backends.borrow().get(&crtc) {
let result = DrmRenderer::render_surface( let result = DrmRenderer::render_surface(
&mut *surface.borrow_mut(), &mut *surface.borrow_mut(),
&self.buffer_utils,
self.device_id, self.device_id,
crtc, crtc,
&mut *self.window_map.borrow_mut(), &mut *self.window_map.borrow_mut(),
@ -748,6 +745,7 @@ impl DrmRenderer {
fn render_surface( fn render_surface(
surface: &mut RenderSurface, surface: &mut RenderSurface,
buffer_utils: &BufferUtils,
device_id: dev_t, device_id: dev_t,
crtc: crtc::Handle, crtc: crtc::Handle,
window_map: &mut MyWindowMap, window_map: &mut MyWindowMap,
@ -778,6 +776,8 @@ impl DrmRenderer {
// draw the surfaces // draw the surfaces
draw_windows( draw_windows(
surface, surface,
device_id,
buffer_utils,
window_map, window_map,
Some(Rectangle { Some(Rectangle {
x: x as i32, x: x as i32,
@ -800,7 +800,7 @@ impl DrmRenderer {
{ {
if let Some(ref wl_surface) = dnd_icon.as_ref() { if let Some(ref wl_surface) = dnd_icon.as_ref() {
if wl_surface.as_ref().is_alive() { 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 { if let CursorImageStatus::Image(ref wl_surface) = *cursor_status {
draw_cursor( draw_cursor(
surface, surface,
device_id,
buffer_utils,
wl_surface, wl_surface,
(ptr_x, ptr_y), (ptr_x, ptr_y),
compositor_token.clone(), compositor_token.clone(),

View File

@ -7,7 +7,6 @@ use smithay::{
reexports::{ reexports::{
calloop::EventLoop, calloop::EventLoop,
wayland_server::{protocol::wl_output, Display}, wayland_server::{protocol::wl_output, Display},
winit::window::CursorIcon,
}, },
wayland::{ wayland::{
output::{Mode, Output, PhysicalProperties}, output::{Mode, Output, PhysicalProperties},
@ -30,7 +29,6 @@ pub fn run_winit(
slog::crit!(log, "Failed to initialize Winit backend: {}", err); slog::crit!(log, "Failed to initialize Winit backend: {}", err);
})?; })?;
/*
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let egl_buffer_reader = Rc::new(RefCell::new( let egl_buffer_reader = Rc::new(RefCell::new(
if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) { if let Ok(egl_buffer_reader) = renderer.bind_wl_display(&display.borrow()) {
@ -44,7 +42,6 @@ pub fn run_winit(
#[cfg(feature = "egl")] #[cfg(feature = "egl")]
let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone()); let buffer_utils = BufferUtils::new(egl_buffer_reader, log.clone());
#[cfg(not(feature = "egl"))] #[cfg(not(feature = "egl"))]
*/
let buffer_utils = BufferUtils::new(log.clone()); let buffer_utils = BufferUtils::new(log.clone());
let (w, h): (u32, u32) = renderer.window_size().physical_size.into(); let (w, h): (u32, u32) = renderer.window_size().physical_size.into();
@ -56,7 +53,7 @@ pub fn run_winit(
let mut state = AnvilState::init( let mut state = AnvilState::init(
display.clone(), display.clone(),
event_loop.handle(), event_loop.handle(),
buffer_utils, buffer_utils.clone(),
None, None,
None, None,
log.clone(), 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"); renderer.clear([0.8, 0.8, 0.9, 1.0]).expect("Failed to clear frame");
// draw the windows // 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(); let (x, y) = *state.pointer_location.borrow();
// draw the dnd icon if any // draw the dnd icon if any
@ -124,7 +121,7 @@ pub fn run_winit(
let guard = state.dnd_icon.lock().unwrap(); let guard = state.dnd_icon.lock().unwrap();
if let Some(ref surface) = *guard { if let Some(ref surface) = *guard {
if surface.as_ref().is_alive() { 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 // draw as relevant
if let CursorImageStatus::Image(ref surface) = *guard { if let CursorImageStatus::Image(ref surface) = *guard {
renderer.window().set_cursor_visible(false); 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 { } else {
renderer.window().set_cursor_visible(true); renderer.window().set_cursor_visible(true);
} }