diff --git a/examples/drm.rs b/examples/drm.rs index 197dcc7..f6d1f25 100644 --- a/examples/drm.rs +++ b/examples/drm.rs @@ -13,25 +13,41 @@ extern crate slog_term; mod helpers; +use drm::Device as BasicDevice; use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; use drm::control::crtc; use drm::control::encoder::Info as EncoderInfo; use drm::result::Error as DrmError; -use glium::Surface; -use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; +use glium::{Blend, Surface}; +use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData, Buffer}; use slog::{Drain, Logger}; use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler}; use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::graphics::egl::wayland::{Format, EGLDisplay, EGLWaylandExtensions}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::wayland::compositor::roles::Role; use smithay::wayland::shell::ShellState; use smithay::wayland::shm::init_shm_global; use std::cell::RefCell; -use std::fs::OpenOptions; +use std::fs::{File, OpenOptions}; +use std::os::unix::io::RawFd; +use std::os::unix::io::AsRawFd; use std::rc::Rc; use std::time::Duration; -use wayland_server::{StateToken, StateProxy}; +use wayland_server::{StateToken}; + +#[derive(Debug)] +pub struct Card(File); + +impl AsRawFd for Card { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl BasicDevice for Card {} +impl ControlDevice for Card {} fn main() { // A logger facility, here we use the terminal for this example @@ -50,8 +66,8 @@ fn main() { let mut options = OpenOptions::new(); options.read(true); options.write(true); - let mut device: DrmDevice> = - DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap(); + let mut device = + DrmDevice::new(Card(options.clone().open("/dev/dri/card0").unwrap()), log.clone()).unwrap(); // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -81,27 +97,35 @@ fn main() { // Assuming we found a good connector and loaded the info into `connector_info` let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) + // Initialize the hardware backend + let renderer = GliumDrawer::from(device + .create_backend(crtc, mode, vec![connector_info.handle()]) + .unwrap()); { - // Initialize the hardware backend - let renderer = device - .create_backend(event_loop.state(), crtc, mode, vec![connector_info.handle()]) - .unwrap(); - /* * Initialize glium */ - let mut frame = event_loop.state().get(renderer).draw(); + let mut frame = renderer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.finish().unwrap(); } + let egl_display = Rc::new(RefCell::new( + if let Ok(egl_display) = renderer.bind_wl_display(&display) { + info!(log, "EGL hardware-acceleration enabled"); + Some(egl_display) + } else { + None + } + )); + /* * Initialize the globals */ init_shm_global(&mut event_loop, vec![], log.clone()); - let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); + let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), egl_display.clone()); /* * Add a listening socket: @@ -120,6 +144,7 @@ fn main() { shell_state_token, compositor_token, window_map: window_map.clone(), + drawer: renderer, logger: log, }, ).unwrap(); @@ -133,22 +158,20 @@ fn main() { } pub struct DrmHandlerImpl { - shell_state_token: StateToken>, - compositor_token: CompositorToken, + shell_state_token: StateToken>>, ()>>, + compositor_token: CompositorToken>>>, window_map: Rc>, + drawer: GliumDrawer>, logger: ::slog::Logger, } -impl DrmHandler> for DrmHandlerImpl { - fn ready<'a, S: Into>>(&mut self, state: S, _device: &mut DrmDevice>, - backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { - let state = state.into(); - let drawer = state.get(backend); - let mut frame = drawer.draw(); +impl DrmHandler for DrmHandlerImpl { + fn ready(&mut self, _device: &mut DrmDevice, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { + let mut frame = self.drawer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); // redraw the frame, in a simple but inneficient way { - let screen_dimensions = drawer.get_framebuffer_dimensions(); + let screen_dimensions = self.drawer.get_framebuffer_dimensions(); self.window_map .borrow() .with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { @@ -159,18 +182,52 @@ impl DrmHandler> for DrmHandlerImpl { wl_surface, initial_place, |_surface, attributes, role, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { - // there is actually something to draw ! + // there is actually something to draw ! + if attributes.user_data.texture.is_none() { + let mut remove = false; + match attributes.user_data.buffer { + Some(Buffer::Egl { ref images }) => { + match images.format { + Format::RGB | Format::RGBA => { + attributes.user_data.texture = self.drawer.texture_from_egl(&images); + }, + _ => { + // we don't handle the more complex formats here. + attributes.user_data.texture = None; + remove = true; + }, + }; + }, + Some(Buffer::Shm { ref data, ref size }) => { + attributes.user_data.texture = Some(self.drawer.texture_from_mem(data, *size)); + }, + _ => {}, + } + if remove { + attributes.user_data.buffer = None; + } + } + + if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { x += subdata.x; y += subdata.y; } - drawer.render( + info!(self.logger, "Render window"); + self.drawer.render_texture( &mut frame, - contents, - (w, h), + texture, + match *attributes.user_data.buffer.as_ref().unwrap() { + Buffer::Egl { ref images } => images.y_inverted, + Buffer::Shm { .. } => false, + }, + match *attributes.user_data.buffer.as_ref().unwrap() { + Buffer::Egl { ref images } => (images.width, images.height), + Buffer::Shm { ref size, .. } => *size, + }, (x, y), screen_dimensions, + Blend::alpha_blending(), ); TraversalAction::DoChildren((x, y)) } else { @@ -186,7 +243,7 @@ impl DrmHandler> for DrmHandlerImpl { frame.finish().unwrap(); } - fn error<'a, S: Into>>(&mut self, _state: S, _device: &mut DrmDevice>, + fn error(&mut self, _device: &mut DrmDevice, error: DrmError) { panic!("{:?}", error); } diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs index ac17e7c..814a81e 100644 --- a/examples/helpers/glium.rs +++ b/examples/helpers/glium.rs @@ -4,10 +4,13 @@ use glium::backend::Facade; use glium::index::PrimitiveType; use glium::texture::{MipmapsOption, UncompressedFloatFormat, Texture2d}; use smithay::backend::graphics::egl::EGLGraphicsBackend; -use smithay::backend::graphics::egl::wayland::{Format, EGLImages}; +use smithay::backend::graphics::egl::error::Result as EGLResult; +use smithay::backend::graphics::egl::wayland::{Format, EGLImages, EGLDisplay, EGLWaylandExtensions, BufferAccessError}; use smithay::backend::graphics::glium::GliumGraphicsBackend; use std::borrow::Borrow; use std::ops::Deref; +use wayland_server::Display; +use wayland_server::protocol::wl_buffer::WlBuffer; #[derive(Copy, Clone)] struct Vertex { @@ -135,15 +138,14 @@ impl GliumDrawer { images.width, images.height, ).unwrap(); - if let Err(_) = unsafe { self.display.get_context().exec_in_context(|| { - images.bind_to_texture(0, opengl_texture.get_id()) - }) } { return None }; + unsafe { images.bind_to_texture(0, opengl_texture.get_id()).expect("Failed to bind to texture"); } Some(opengl_texture) } pub fn render_texture(&self, target: &mut glium::Frame, texture: &Texture2d, y_inverted: bool, surface_dimensions: (u32, u32), - surface_location: (i32, i32), screen_size: (u32, u32)) + surface_location: (i32, i32), screen_size: (u32, u32), + blending: glium::Blend) { let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32); @@ -173,24 +175,9 @@ impl GliumDrawer { &self.program, &uniforms, &glium::DrawParameters { - blend: glium::Blend { - color: glium::BlendingFunction::Addition { - source: glium::LinearBlendingFactor::One, - destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - alpha: glium::BlendingFunction::Addition { - source: glium::LinearBlendingFactor::One, - destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, - }, - ..Default::default() - }, - depth: glium::Depth { - test: glium::DepthTest::IfLess, - write: false, - ..Default::default() - }, - .. Default::default() - }, + blend: blending, + ..Default::default() + } ) .unwrap(); } @@ -200,3 +187,12 @@ impl GliumDrawer { self.display.draw() } } + +impl EGLWaylandExtensions for GliumDrawer { + fn bind_wl_display(&self, display: &Display) -> EGLResult { + self.display.bind_wl_display(display) + } + fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> { + self.display.unbind_wl_display(display) + } +} diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs index 9d8b4ba..cee9b35 100644 --- a/examples/helpers/implementations.rs +++ b/examples/helpers/implementations.rs @@ -1,8 +1,8 @@ -use super::{GliumDrawer, WindowMap}; +use super::WindowMap; use smithay::backend::graphics::egl::wayland::{Format, BufferAccessError}; use glium::texture::Texture2d; use rand; -use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImages}; +use smithay::backend::graphics::egl::wayland::{EGLDisplay, EGLImages}; use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceUserImplementation}; use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, @@ -10,7 +10,6 @@ use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfa use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; use std::cell::RefCell; use std::rc::Rc; -use std::borrow::Borrow; use wayland_server::{EventLoop, StateToken}; define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); @@ -28,17 +27,22 @@ pub enum Buffer { unsafe impl Send for Buffer {} -pub fn surface_implementation() -> SurfaceUserImplementation>> { +pub fn surface_implementation() -> SurfaceUserImplementation>>> { SurfaceUserImplementation { - commit: |_, drawer, surface, token| { + commit: |_, display, surface, token| { // we retrieve the contents of the associated buffer and copy it token.with_surface_data(surface, |attributes| { match attributes.buffer.take() { Some(Some((buffer, (_x, _y)))) => { // we ignore hotspot coordinates in this simple example - match as Borrow>::borrow(&**drawer).egl_buffer_contents(buffer) { + match if let Some(display) = display.borrow().as_ref() { + display.egl_buffer_contents(buffer) + } else { + Err(BufferAccessError::NotManaged(buffer)) + } + { Ok(images) => { - let format = match images.format { + match images.format { Format::RGB => {}, Format::RGBA => {}, _ => { @@ -48,7 +52,7 @@ pub fn surface_implementation() -> SurfaceUserI return; }, }; - attributes.user_data.texture = drawer.texture_from_egl(&images); + attributes.user_data.texture = None; attributes.user_data.buffer = Some(Buffer::Egl { images }); }, Err(BufferAccessError::NotManaged(buffer)) => { @@ -62,9 +66,9 @@ pub fn surface_implementation() -> SurfaceUserI new_vec .extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]); } - attributes.user_data.texture = Some(drawer.texture_from_mem(&new_vec, (data.width as u32, data.height as u32))); + attributes.user_data.texture = None; attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) }); - }).unwrap(); + }).expect("Got EGL buffer with no set EGLDisplay. You need to unbind your EGLContexts before dropping them!"); buffer.release(); }, Err(err) => panic!("EGL error: {}", err), @@ -85,15 +89,14 @@ pub fn surface_implementation() -> SurfaceUserI } } -pub struct ShellIData { - pub token: CompositorToken>>, - pub window_map: Rc>, (), F>>>, +pub struct ShellIData { + pub token: CompositorToken>>>, + pub window_map: Rc>>, (), F>>>, } -pub fn shell_implementation() -> ShellSurfaceUserImplementation>, ShellIData, ()> +pub fn shell_implementation() -> ShellSurfaceUserImplementation>>, ShellIData, ()> where F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, - G: EGLGraphicsBackend + 'static, { ShellSurfaceUserImplementation { new_client: |_, _, _| {}, @@ -145,20 +148,20 @@ fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { .map(|(x, y)| (x as i32, y as i32)) } -pub type MyWindowMap = WindowMap< +pub type MyWindowMap = WindowMap< SurfaceData, Roles, - Rc>, + Rc>>, (), fn(&SurfaceAttributes) -> Option<(i32, i32)>, >; -pub fn init_shell( - evl: &mut EventLoop, log: ::slog::Logger, data: Rc>) +pub fn init_shell( + evl: &mut EventLoop, log: ::slog::Logger, data: Rc>>) -> ( - CompositorToken>>, - StateToken>, ()>>, - Rc>>, + CompositorToken>>>, + StateToken>>, ()>>, + Rc>, ) { let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone()); diff --git a/examples/udev.rs b/examples/udev.rs index 830d178..2aac748 100644 --- a/examples/udev.rs +++ b/examples/udev.rs @@ -19,24 +19,26 @@ extern crate ctrlc; mod helpers; +use drm::Device as BasicDevice; use drm::control::{Device as ControlDevice, ResourceInfo}; use drm::control::connector::{Info as ConnectorInfo, State as ConnectorState}; use drm::control::encoder::Info as EncoderInfo; use drm::control::crtc; use drm::result::Error as DrmError; -use glium::Surface; +use glium::{Blend, Surface}; use image::{ImageBuffer, Rgba}; use libinput::{Libinput, Device as LibinputDevice, event}; use libinput::event::keyboard::KeyboardEventTrait; -use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData}; +use helpers::{init_shell, GliumDrawer, MyWindowMap, Roles, SurfaceData, Buffer}; use slog::{Drain, Logger}; -use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler}; +use smithay::backend::drm::{DrmBackend, DrmDevice, DrmHandler, DevPath}; use smithay::backend::graphics::GraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, Format}; use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerButtonEvent, PointerAxisEvent, KeyState}; use smithay::backend::libinput::{LibinputInputBackend, libinput_bind, PointerAxisEvent as LibinputPointerAxisEvent, LibinputSessionInterface}; -use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind}; +use smithay::backend::udev::{UdevBackend, UdevHandler, udev_backend_bind, primary_gpu, SessionFdDrmDevice}; use smithay::backend::session::{Session, SessionNotifier}; use smithay::backend::session::direct::{direct_session_bind, DirectSession}; use smithay::wayland::compositor::{CompositorToken, SubsurfaceRole, TraversalAction}; @@ -46,15 +48,17 @@ use smithay::wayland::seat::{KeyboardHandle, PointerHandle, Seat}; use smithay::wayland::shell::ShellState; use smithay::wayland::shm::init_shm_global; use std::cell::RefCell; -use std::collections::HashSet; +use std::collections::HashMap; use std::io::Error as IoError; use std::rc::Rc; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; +use std::path::PathBuf; use std::process::Command; +use std::os::unix::io::{AsRawFd, RawFd}; use xkbcommon::xkb::keysyms as xkb; -use wayland_server::{StateToken, StateProxy}; +use wayland_server::{Display, StateToken, StateProxy}; use wayland_server::protocol::{wl_output, wl_pointer}; struct LibinputInputHandler { @@ -172,6 +176,8 @@ impl InputHandler for LibinputInputHandler { } fn main() { + let active_egl_context = Rc::new(RefCell::new(None)); + // A logger facility, here we use the terminal for this example let log = Logger::root( slog_term::FullFormat::new(slog_term::PlainSyncDecorator::new(std::io::stdout())).build().fuse(), @@ -181,12 +187,19 @@ fn main() { // Initialize the wayland server let (mut display, mut event_loop) = wayland_server::create_display(); + /* + * Add a listening socket + */ + let name = display.add_socket_auto().unwrap().into_string().unwrap(); + println!("Listening on socket: {}", name); + let display = Rc::new(display); + /* * Initialize the compositor */ init_shm_global(&mut event_loop, vec![], log.clone()); - let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); + let (compositor_token, shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), active_egl_context.clone()); /* * Initialize session on the current tty @@ -206,11 +219,19 @@ fn main() { * Initialize the udev backend */ let context = udev::Context::new().unwrap(); + let seat = session.seat(); + + let primary_gpu = primary_gpu(&context, &seat).unwrap_or_default(); + let bytes = include_bytes!("resources/cursor2.rgba"); let udev_token = UdevBackend::new(&mut event_loop, &context, session.clone(), UdevHandlerImpl { shell_state_token, compositor_token, + active_egl_context, + backends: HashMap::new(), + display: display.clone(), + primary_gpu, window_map: window_map.clone(), pointer_location: pointer_location.clone(), pointer_image: ImageBuffer::from_raw(64, 64, bytes.to_vec()).unwrap(), @@ -266,7 +287,6 @@ fn main() { /* * Initialize libinput backend */ - let seat = session.seat(); let mut libinput_context = Libinput::new_from_udev::>>>(session.into(), &context); let libinput_session_id = notifier.register(libinput_context.clone()); libinput_context.udev_assign_seat(&seat).unwrap(); @@ -286,12 +306,6 @@ fn main() { let session_event_source = direct_session_bind(notifier, &mut event_loop, log.clone()).unwrap(); let udev_event_source = udev_backend_bind(&mut event_loop, udev_token).unwrap(); - /* - * Add a listening socket - */ - let name = display.add_socket_auto().unwrap().into_string().unwrap(); - println!("Listening on socket: {}", name); - while running.load(Ordering::SeqCst) { event_loop.dispatch(Some(16)); display.flush_clients(); @@ -312,8 +326,12 @@ fn main() { } struct UdevHandlerImpl { - shell_state_token: StateToken>, - compositor_token: CompositorToken, + shell_state_token: StateToken>>, ()>>, + compositor_token: CompositorToken>>>, + active_egl_context: Rc>>, + backends: HashMap>>>>>, + display: Rc, + primary_gpu: Option, window_map: Rc>, pointer_location: Rc>, pointer_image: ImageBuffer, Vec>, @@ -321,7 +339,7 @@ struct UdevHandlerImpl { } impl UdevHandlerImpl { - pub fn scan_connectors<'a, S: Into>>(&self, state: S, device: &mut DrmDevice>) { + pub fn scan_connectors(&self, device: &mut DrmDevice) -> HashMap>> { // Get a set of all modesetting resource handles (excluding planes): let res_handles = device.resource_handles().unwrap(); @@ -336,72 +354,78 @@ impl UdevHandlerImpl { .inspect(|conn| info!(self.logger, "Connected: {:?}", conn.connector_type())) .collect(); - let mut used_crtcs: HashSet = HashSet::new(); - - let mut state = state.into(); + let mut backends = HashMap::new(); // very naive way of finding good crtc/encoder/connector combinations. This problem is np-complete for connector_info in connector_infos { let encoder_infos = connector_info.encoders().iter().flat_map(|encoder_handle| EncoderInfo::load_from_device(device, *encoder_handle)).collect::>(); for encoder_info in encoder_infos { for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) { - if !used_crtcs.contains(&crtc) { + if !backends.contains_key(&crtc) { let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) // create a backend - let renderer_token = device.create_backend(&mut state, crtc, mode, vec![connector_info.handle()]).unwrap(); + let renderer = GliumDrawer::from(device.create_backend(crtc, mode, vec![connector_info.handle()]).unwrap()); // create cursor - { - let renderer = state.get_mut(renderer_token); - renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap(); - } + renderer.set_cursor_representation(&self.pointer_image, (2, 2)).unwrap(); // render first frame { - let renderer = state.get_mut(renderer_token); let mut frame = renderer.draw(); frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.finish().unwrap(); } - used_crtcs.insert(crtc); + backends.insert(crtc, renderer); break; } } } } + + backends } } -impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { - fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice>) -> Option +impl UdevHandler for UdevHandlerImpl { + fn device_added<'a, S: Into>>(&mut self, _state: S, device: &mut DrmDevice) -> Option { - self.scan_connectors(state, device); + // init hardware acceleration on the primary gpu. + if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu { + *self.active_egl_context.borrow_mut() = device.bind_wl_display(&*self.display).ok(); + } + + let backends = Rc::new(RefCell::new(self.scan_connectors(device))); + self.backends.insert(device.device_id(), backends.clone()); Some(DrmHandlerImpl { shell_state_token: self.shell_state_token.clone(), compositor_token: self.compositor_token.clone(), + backends, window_map: self.window_map.clone(), pointer_location: self.pointer_location.clone(), logger: self.logger.clone(), }) } - fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>>) { - //quick and dirt, just re-init the device + fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>) { + //quick and dirt, just re-init all backends let mut state = state.into(); - self.device_removed(&mut state, device); - state.with_value(device, |state, device| self.scan_connectors(state, device)); + let backends = self.backends.get(&state.get(device).device_id()).unwrap(); + *backends.borrow_mut() = self.scan_connectors(state.get_mut(device)); } - fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>>) { - state.into().with_value(device, |state, device| { - let crtcs = device.current_backends().into_iter().map(|backend| state.get(backend).crtc()).collect::>(); - let mut state: StateProxy = state.into(); - for crtc in crtcs { - device.destroy_backend(&mut state, &crtc); - } - }); + fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>) { + let state = state.into(); + let device = state.get(device); + + // drop the backends on this side + self.backends.remove(&device.device_id()); + + // don't use hardware acceleration anymore, if this was the primary gpu + if device.dev_path().and_then(|path| path.canonicalize().ok()) == self.primary_gpu { + *self.active_egl_context.borrow_mut() = None; + } } fn error<'a, S: Into>>(&mut self, _state: S, error: IoError) { @@ -410,28 +434,27 @@ impl UdevHandler, DrmHandlerImpl> for UdevHandlerImpl { } pub struct DrmHandlerImpl { - shell_state_token: StateToken>, - compositor_token: CompositorToken, + shell_state_token: StateToken>>, ()>>, + compositor_token: CompositorToken>>>, + backends: Rc>>>>, window_map: Rc>, pointer_location: Rc>, logger: ::slog::Logger, } -impl DrmHandler> for DrmHandlerImpl { - fn ready<'a, S: Into>>(&mut self, state: S, _device: &mut DrmDevice>, - backend: &StateToken>, _crtc: crtc::Handle, _frame: u32, _duration: Duration) { - let state = state.into(); - let drawer = state.get(backend); - { - let (x, y) = *self.pointer_location.borrow(); - let _ = (**drawer).set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); - } - let mut frame = drawer.draw(); - frame.clear_color(0.8, 0.8, 0.9, 1.0); - // redraw the frame, in a simple but inneficient way - { - let screen_dimensions = drawer.get_framebuffer_dimensions(); - self.window_map +impl DrmHandler for DrmHandlerImpl { + fn ready(&mut self, _device: &mut DrmDevice, crtc: crtc::Handle, _frame: u32, _duration: Duration) { + if let Some(drawer) = self.backends.borrow().get(&crtc) { + { + let (x, y) = *self.pointer_location.borrow(); + let _ = drawer.set_cursor_position(x.trunc().abs() as u32, y.trunc().abs() as u32); + } + let mut frame = drawer.draw(); + frame.clear_color(0.8, 0.8, 0.9, 1.0); + // redraw the frame, in a simple but inneficient way + { + let screen_dimensions = drawer.get_framebuffer_dimensions(); + self.window_map .borrow() .with_windows_from_bottom_to_top(|toplevel_surface, initial_place| { if let Some(wl_surface) = toplevel_surface.get_surface() { @@ -441,18 +464,52 @@ impl DrmHandler> for DrmHandlerImpl { wl_surface, initial_place, |_surface, attributes, role, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { - // there is actually something to draw ! + // there is actually something to draw ! + if attributes.user_data.texture.is_none() { + let mut remove = false; + match attributes.user_data.buffer { + Some(Buffer::Egl { ref images }) => { + match images.format { + Format::RGB | Format::RGBA => { + attributes.user_data.texture = drawer.texture_from_egl(&images); + }, + _ => { + // we don't handle the more complex formats here. + attributes.user_data.texture = None; + remove = true; + }, + }; + }, + Some(Buffer::Shm { ref data, ref size }) => { + attributes.user_data.texture = Some(drawer.texture_from_mem(data, *size)); + }, + _ => {}, + } + if remove { + attributes.user_data.buffer = None; + } + } + + if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { x += subdata.x; y += subdata.y; } - drawer.render( + info!(self.logger, "Render window"); + drawer.render_texture( &mut frame, - contents, - (w, h), + texture, + match *attributes.user_data.buffer.as_ref().unwrap() { + Buffer::Egl { ref images } => images.y_inverted, + Buffer::Shm { .. } => false, + }, + match *attributes.user_data.buffer.as_ref().unwrap() { + Buffer::Egl { ref images } => (images.width, images.height), + Buffer::Shm { ref size, .. } => *size, + }, (x, y), screen_dimensions, + Blend::alpha_blending(), ); TraversalAction::DoChildren((x, y)) } else { @@ -464,13 +521,14 @@ impl DrmHandler> for DrmHandlerImpl { .unwrap(); } }); - } - if let Err(err) = frame.finish() { - error!(self.logger, "Error during rendering: {:?}", err); + } + if let Err(err) = frame.finish() { + error!(self.logger, "Error during rendering: {:?}", err); + } } } - fn error<'a, S: Into>>(&mut self, _state: S, _device: &mut DrmDevice>, + fn error(&mut self, _device: &mut DrmDevice, error: DrmError) { error!(self.logger, "{:?}", error); } diff --git a/examples/winit.rs b/examples/winit.rs index 632e86a..f8e43f7 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -15,6 +15,7 @@ use glium::Surface; use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer}; use slog::{Drain, Logger}; use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::graphics::egl::wayland::{EGLWaylandExtensions, Format}; use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent}; use smithay::backend::winit; @@ -27,23 +28,23 @@ use std::cell::RefCell; use std::rc::Rc; use wayland_server::protocol::{wl_output, wl_pointer}; -struct WinitInputHandler { +struct WinitInputHandler { log: Logger, pointer: PointerHandle, keyboard: KeyboardHandle, - window_map: Rc>>, + window_map: Rc>, pointer_location: (f64, f64), serial: u32, } -impl WinitInputHandler { +impl WinitInputHandler { fn next_serial(&mut self) -> u32 { self.serial += 1; self.serial } } -impl InputHandler for WinitInputHandler { +impl InputHandler for WinitInputHandler { fn on_seat_created(&mut self, _: &input::Seat) { /* never happens with winit */ } @@ -136,12 +137,17 @@ fn main() { let (mut display, mut event_loop) = wayland_server::create_display(); - if let Ok(_) = renderer.bind_wl_display(&display) { - info!(log, "EGL hardware-acceleration enabled"); - } + let egl_display = Rc::new(RefCell::new( + if let Ok(egl_display) = renderer.bind_wl_display(&display) { + info!(log, "EGL hardware-acceleration enabled"); + Some(egl_display) + } else { + None + } + )); let (w, h) = renderer.get_framebuffer_dimensions(); - let drawer = Rc::new(GliumDrawer::from(renderer)); + let drawer = GliumDrawer::from(renderer); /* * Initialize the globals @@ -149,7 +155,7 @@ fn main() { init_shm_global(&mut event_loop, vec![], log.clone()); - let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), drawer.clone()); + let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), egl_display); let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone()); @@ -227,7 +233,32 @@ fn main() { wl_surface, initial_place, |_surface, attributes, role, &(mut x, mut y)| { - // there is actually something to draw ! + // there is actually something to draw ! + if attributes.user_data.texture.is_none() { + let mut remove = false; + match attributes.user_data.buffer { + Some(Buffer::Egl { ref images }) => { + match images.format { + Format::RGB | Format::RGBA => { + attributes.user_data.texture = drawer.texture_from_egl(&images); + }, + _ => { + // we don't handle the more complex formats here. + attributes.user_data.texture = None; + remove = true; + }, + }; + }, + Some(Buffer::Shm { ref data, ref size }) => { + attributes.user_data.texture = Some(drawer.texture_from_mem(data, *size)); + }, + _ => {}, + } + if remove { + attributes.user_data.buffer = None; + } + } + if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { x += subdata.x; @@ -246,6 +277,17 @@ fn main() { }, (x, y), screen_dimensions, + glium::Blend { + color: glium::BlendingFunction::Addition { + source: glium::LinearBlendingFactor::One, + destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + alpha: glium::BlendingFunction::Addition { + source: glium::LinearBlendingFactor::One, + destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + ..Default::default() + } ); TraversalAction::DoChildren((x, y)) } else { diff --git a/src/backend/drm/backend.rs b/src/backend/drm/backend.rs index 49175d5..2eb9044 100644 --- a/src/backend/drm/backend.rs +++ b/src/backend/drm/backend.rs @@ -1,27 +1,35 @@ use super::error::*; use super::DevPath; use backend::graphics::GraphicsBackend; -use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, EGLImage, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError}; +use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError}; +use backend::graphics::egl::error::Result as EGLResult; use backend::graphics::egl::native::{Gbm, GbmSurfaceArguments}; +use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, BufferAccessError, EGLImages}; use drm::control::{Device, ResourceInfo}; use drm::control::{connector, crtc, encoder, framebuffer, Mode}; use gbm::{Device as GbmDevice, BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle}; use image::{ImageBuffer, Rgba}; -use nix::libc::{c_void, c_uint}; +use nix::libc::c_void; use std::cell::Cell; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use wayland_server::Display; +use wayland_server::protocol::wl_buffer::WlBuffer; + +pub struct DrmBackend { + backend: Rc>, + surface: EGLSurface>, + mode: Mode, + connectors: Vec, +} /// Backend based on a `DrmDevice` and a given crtc -pub struct DrmBackend { +pub(crate) struct DrmBackendInternal { context: Rc, GbmDevice>>, - surface: EGLSurface>, cursor: Cell>, + current_frame_buffer: Cell, front_buffer: Cell>, next_buffer: Cell>>, crtc: crtc::Handle, - mode: Mode, - connectors: Vec, logger: ::slog::Logger, } @@ -31,10 +39,9 @@ impl DrmBackend { crtc: crtc::Handle, mode: Mode, connectors: Vec, - logger: ::slog::Logger, + log: ::slog::Logger, ) -> Result { // logger already initialized by the DrmDevice - let log = ::slog_or_stdlog(logger); info!(log, "Initializing DrmBackend"); let (w, h) = mode.size(); @@ -89,26 +96,24 @@ impl DrmBackend { ).chain_err(|| ErrorKind::GbmInitFailed)?); Ok(DrmBackend { - context, + backend: Rc::new(DrmBackendInternal { + context, + cursor, + current_frame_buffer: Cell::new(fb), + front_buffer: Cell::new(front_bo), + next_buffer: Cell::new(None), + crtc, + logger: log, + }), surface, - cursor, - front_buffer: Cell::new(front_bo), - next_buffer: Cell::new(None), - crtc, mode, connectors, - logger: log, }) } - pub(crate) fn unlock_buffer(&self) { - // after the page swap is finished we need to release the rendered buffer. - // this is called from the PageFlipHandler - if let Some(next_buffer) = self.next_buffer.replace(None) { - trace!(self.logger, "Releasing old front buffer"); - self.front_buffer.set(next_buffer); - // drop and release the old buffer - } + pub(crate) fn weak(&self) -> Weak> + { + Rc::downgrade(&self.backend) } /// Add a connector to backend @@ -117,8 +122,8 @@ impl DrmBackend { /// /// Errors if the new connector does not support the currently set `Mode` pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> { - let info = connector::Info::load_from_device(&*self.context, connector) - .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))?; + let info = connector::Info::load_from_device(&*self.backend.context, connector) + .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.backend.context.dev_path())))?; // check if the connector can handle the current mode if info.modes().contains(&self.mode) { @@ -126,28 +131,28 @@ impl DrmBackend { let encoders = info.encoders() .iter() .map(|encoder| { - encoder::Info::load_from_device(&*self.context, *encoder) - .chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.context.dev_path()))) + encoder::Info::load_from_device(&*self.backend.context, *encoder) + .chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.backend.context.dev_path()))) }) .collect::>>()?; // and if any encoder supports the selected crtc - let resource_handles = self.context + let resource_handles = self.backend.context .resource_handles() - .chain_err(|| ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.context.dev_path())))?; + .chain_err(|| ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.backend.context.dev_path())))?; if !encoders .iter() .map(|encoder| encoder.possible_crtcs()) .all(|crtc_list| { resource_handles .filter_crtcs(crtc_list) - .contains(&self.crtc) + .contains(&self.backend.crtc) }) { - bail!(ErrorKind::NoSuitableEncoder(info, self.crtc)); + bail!(ErrorKind::NoSuitableEncoder(info, self.backend.crtc)); } info!( - self.logger, + self.backend.logger, "Adding new connector: {:?}", info.connector_type() ); @@ -165,14 +170,14 @@ impl DrmBackend { /// Removes a currently set connector pub fn remove_connector(&mut self, connector: connector::Handle) { - if let Ok(info) = connector::Info::load_from_device(&*self.context, connector) { + if let Ok(info) = connector::Info::load_from_device(&*self.backend.context, connector) { info!( - self.logger, + self.backend.logger, "Removing connector: {:?}", info.connector_type() ); } else { - info!(self.logger, "Removing unknown connector"); + info!(self.backend.logger, "Removing unknown connector"); } self.connectors.retain(|x| *x != connector); @@ -188,8 +193,8 @@ impl DrmBackend { pub fn use_mode(&mut self, mode: Mode) -> Result<()> { // check the connectors for connector in &self.connectors { - if !connector::Info::load_from_device(&*self.context, *connector) - .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))? + if !connector::Info::load_from_device(&*self.backend.context, *connector) + .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.backend.context.dev_path())))? .modes() .contains(&mode) { @@ -197,13 +202,13 @@ impl DrmBackend { } } - info!(self.logger, "Setting new mode: {:?}", mode.name()); + info!(self.backend.logger, "Setting new mode: {:?}", mode.name()); let (w, h) = mode.size(); // Recreate the surface and the related resources to match the new // resolution. - debug!(self.logger, "Reinitializing surface for new mode: {}:{}", w, h); - let surface = self.context.create_surface( + debug!(self.backend.logger, "Reinitializing surface for new mode: {}:{}", w, h); + let surface = self.backend.context.create_surface( GbmSurfaceArguments { size: (w as u32, h as u32), format: GbmFormat::XRGB8888, @@ -224,10 +229,10 @@ impl DrmBackend { // Clean up next_buffer { - if let Some(mut old_bo) = self.next_buffer.take() { + if let Some(mut old_bo) = self.backend.next_buffer.take() { if let Ok(Some(fb)) = old_bo.take_userdata() { - if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) { - warn!(self.logger, "Error releasing old back_buffer framebuffer: {:?}", err); + if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) { + warn!(self.backend.logger, "Error releasing old back_buffer framebuffer: {:?}", err); } } } @@ -235,41 +240,37 @@ impl DrmBackend { // Cleanup front_buffer and init the first screen on the new front_buffer // (must be done before calling page_flip for the first time) - let fb = { - let mut old_front_bo = self.front_buffer.replace( - surface + let mut old_front_bo = self.backend.front_buffer.replace({ + let mut front_bo = surface .lock_front_buffer() - .chain_err(|| ErrorKind::FailedToSwap)? - ); - if let Ok(Some(fb)) = old_front_bo.take_userdata() { - if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) { - warn!(self.logger, "Error releasing old front_buffer framebuffer: {:?}", err); - } - } + .chain_err(|| ErrorKind::FailedToSwap)?; - let front_bo = self.front_buffer.get_mut(); - debug!(self.logger, "FrontBuffer color format: {:?}", front_bo.format()); + debug!(self.backend.logger, "FrontBuffer color format: {:?}", front_bo.format()); // we also need a new framebuffer for the front buffer - let dev_path = self.context.dev_path(); - let fb = framebuffer::create(&*self.context, &**front_bo) + let dev_path = self.backend.context.dev_path(); + let fb = framebuffer::create(&*self.backend.context, &*front_bo) .chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", dev_path)))?; front_bo.set_userdata(fb).unwrap(); - fb - }; - - debug!(self.logger, "Setting screen"); - crtc::set( - &*self.context, - self.crtc, - fb.handle(), - &self.connectors, - (0, 0), - Some(mode), - ).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", self.crtc, self.context.dev_path())))?; + debug!(self.backend.logger, "Setting screen"); + crtc::set( + &*self.backend.context, + self.backend.crtc, + fb.handle(), + &self.connectors, + (0, 0), + Some(mode), + ).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", self.backend.crtc, self.backend.context.dev_path())))?; + front_bo + }); + if let Ok(Some(fb)) = old_front_bo.take_userdata() { + if let Err(err) = framebuffer::destroy(&*self.backend.context, fb.handle()) { + warn!(self.backend.logger, "Error releasing old front_buffer framebuffer: {:?}", err); + } + } // Drop the old surface after cleanup self.surface = surface; @@ -279,7 +280,37 @@ impl DrmBackend { /// Returns the crtc id used by this backend pub fn crtc(&self) -> crtc::Handle { - self.crtc + self.backend.crtc + } +} + +impl DrmBackendInternal { + pub(crate) fn unlock_buffer(&self) { + // after the page swap is finished we need to release the rendered buffer. + // this is called from the PageFlipHandler + if let Some(next_buffer) = self.next_buffer.replace(None) { + trace!(self.logger, "Releasing old front buffer"); + self.front_buffer.set(next_buffer); + // drop and release the old buffer + } + } + + pub(crate) fn page_flip(&self, fb: Option<&framebuffer::Info>) -> ::std::result::Result<(), SwapBuffersError> { + trace!(self.logger, "Queueing Page flip"); + + let fb = *fb.unwrap_or(&self.current_frame_buffer.get()); + + // and flip + crtc::page_flip( + &*self.context, + self.crtc, + fb.handle(), + &[crtc::PageFlipFlags::PageFlipEvent], + ).map_err(|_| SwapBuffersError::ContextLost)?; + + self.current_frame_buffer.set(fb); + + Ok(()) } } @@ -288,7 +319,7 @@ impl Drop for DrmBackend { // Drop framebuffers attached to the userdata of the gbm surface buffers. // (They don't implement drop, as they need the device) if let Ok(Some(fb)) = { - if let Some(mut next) = self.next_buffer.take() { + if let Some(mut next) = self.backend.next_buffer.take() { next.take_userdata() } else if let Ok(mut next) = self.surface.lock_front_buffer() { next.take_userdata() @@ -297,9 +328,13 @@ impl Drop for DrmBackend { } } { // ignore failure at this point - let _ = framebuffer::destroy(&*self.context, fb.handle()); + let _ = framebuffer::destroy(&*self.backend.context, fb.handle()); } + } +} +impl Drop for DrmBackendInternal { + fn drop(&mut self) { if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() { // ignore failure at this point let _ = framebuffer::destroy(&*self.context, fb.handle()); @@ -315,22 +350,23 @@ impl GraphicsBackend for DrmBackend { type Error = Error; fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> { - trace!(self.logger, "Move the cursor to {},{}", x, y); + trace!(self.backend.logger, "Move the cursor to {},{}", x, y); crtc::move_cursor( - &*self.context, - self.crtc, + &*self.backend.context, + self.backend.crtc, (x as i32, y as i32), - ).chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.context.dev_path()))) + ).chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.backend.context.dev_path()))) } fn set_cursor_representation( &self, buffer: &ImageBuffer, Vec>, hotspot: (u32, u32) ) -> Result<()> { let (w, h) = buffer.dimensions(); - debug!(self.logger, "Importing cursor"); + debug!(self.backend.logger, "Importing cursor"); // import the cursor into a buffer we can render let mut cursor = self + .backend .context .create_buffer_object( w, @@ -344,37 +380,37 @@ impl GraphicsBackend for DrmBackend { .chain_err(|| ErrorKind::GbmInitFailed)? .chain_err(|| ErrorKind::GbmInitFailed)?; - trace!(self.logger, "Setting the new imported cursor"); + trace!(self.backend.logger, "Setting the new imported cursor"); // and set it if crtc::set_cursor2( - &*self.context, - self.crtc, + &*self.backend.context, + self.backend.crtc, &cursor, (hotspot.0 as i32, hotspot.1 as i32), ).is_err() { - crtc::set_cursor(&*self.context, self.crtc, &cursor).chain_err( - || ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.context.dev_path())), + crtc::set_cursor(&*self.backend.context, self.backend.crtc, &cursor).chain_err( + || ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.backend.context.dev_path())), )?; } // and store it - self.cursor.set(cursor); + self.backend.cursor.set(cursor); Ok(()) } } impl EGLGraphicsBackend for DrmBackend { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { - // We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done if { - let nb = self.next_buffer.take(); + let nb = self.backend.next_buffer.take(); let res = nb.is_some(); - self.next_buffer.set(nb); + self.backend.next_buffer.set(nb); res } { - warn!(self.logger, "Tried to swap a DrmBackend with a queued flip"); + // We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done + warn!(self.backend.logger, "Tried to swap a DrmBackend with a queued flip"); return Err(SwapBuffersError::AlreadySwapped); } @@ -386,8 +422,8 @@ impl EGLGraphicsBackend for DrmBackend { // neither weston, wlc or wlroots bother with that as well. // so we just assume we got at least two buffers to do flipping. let mut next_bo = self.surface - .lock_front_buffer() - .expect("Surface only has one front buffer. Not supported by smithay"); + .lock_front_buffer() + .expect("Surface only has one front buffer. Not supported by smithay"); // create a framebuffer if the front buffer does not have one already // (they are reused by gbm) @@ -395,26 +431,18 @@ impl EGLGraphicsBackend for DrmBackend { let fb = if let Some(info) = maybe_fb { info } else { - let fb = framebuffer::create(&*self.context, &*next_bo) + let fb = framebuffer::create(&*self.backend.context, &*next_bo) .map_err(|_| SwapBuffersError::ContextLost)?; next_bo.set_userdata(fb).unwrap(); fb }; - self.next_buffer.set(Some(next_bo)); + self.backend.next_buffer.set(Some(next_bo)); - trace!(self.logger, "Queueing Page flip"); - - // and flip - crtc::page_flip( - &*self.context, - self.crtc, - fb.handle(), - &[crtc::PageFlipFlags::PageFlipEvent], - ).map_err(|_| SwapBuffersError::ContextLost) + self.backend.page_flip(Some(&fb)) } unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { - self.context.get_proc_address(symbol) + self.backend.context.get_proc_address(symbol) } fn get_framebuffer_dimensions(&self) -> (u32, u32) { @@ -423,7 +451,7 @@ impl EGLGraphicsBackend for DrmBackend { } fn is_current(&self) -> bool { - self.context.is_current() && self.surface.is_current() + self.backend.context.is_current() && self.surface.is_current() } unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { @@ -431,19 +459,16 @@ impl EGLGraphicsBackend for DrmBackend { } fn get_pixel_format(&self) -> PixelFormat { - self.context.get_pixel_format() + self.backend.context.get_pixel_format() + } +} + +impl EGLWaylandExtensions for DrmBackend { + fn bind_wl_display(&self, display: &Display) -> EGLResult { + self.backend.context.bind_wl_display(display) + } + + fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> { + self.backend.context.unbind_wl_display(display) } - - fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> { - self.context.bind_wl_display(display).map_err(EglExtensionNotSupportedError::from) - } - - fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> { - self.context.unbind_wl_display(display).map_err(EglExtensionNotSupportedError::from) - } - - /*unsafe fn egl_image_to_texture(&self, image: EGLImage, tex_id: c_uint) -> ::std::result::Result<(), EglExtensionNotSupportedError> { - self.graphics.head().rent(|context| context.egl_image_to_texture(image, tex_id))?; - Ok(()) - }*/ } diff --git a/src/backend/drm/error.rs b/src/backend/drm/error.rs index 681982b..3c9b589 100644 --- a/src/backend/drm/error.rs +++ b/src/backend/drm/error.rs @@ -18,6 +18,11 @@ error_chain! { display("The drm device ({:?}) encountered an access error", dev), } + #[doc = "Unable to determine device id of drm device"] + UnableToGetDeviceId { + description("Unable to determine device id of drm device"), + } + #[doc = "Creation of gbm resource failed"] GbmInitFailed { description("Creation of gbm resource failed"), diff --git a/src/backend/drm/mod.rs b/src/backend/drm/mod.rs index 7ad355d..a109d19 100644 --- a/src/backend/drm/mod.rs +++ b/src/backend/drm/mod.rs @@ -188,9 +188,11 @@ //! ``` #[cfg(feature = "backend_session")] -use backend::graphics::egl::EGLGraphicsBackend; +use backend::graphics::egl::EglExtensionNotSupportedError; use backend::graphics::egl::context::{EGLContext, GlAttributes, PixelFormatRequirements}; +use backend::graphics::egl::error::Result as EGLResult; use backend::graphics::egl::native::Gbm; +use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLDisplay, BufferAccessError, EGLImages}; #[cfg(feature = "backend_session")] use backend::session::SessionObserver; use drm::Device as BasicDevice; @@ -200,36 +202,41 @@ use drm::result::Error as DrmError; use drm::control::framebuffer; use gbm::Device as GbmDevice; use nix; -use std::borrow::Borrow; use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::io::Result as IoResult; use std::os::unix::io::{AsRawFd, RawFd}; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::sync::{Once, ONCE_INIT}; use std::path::PathBuf; use std::time::Duration; -use wayland_server::{EventLoopHandle, StateProxy, StateToken}; +use nix::sys::stat::{dev_t, fstat}; +use wayland_server::{EventLoopHandle, StateToken}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; +#[cfg(feature = "backend_session")] +use wayland_server::{Display, StateProxy}; +use wayland_server::protocol::wl_buffer::WlBuffer; mod backend; pub mod error; pub use self::backend::DrmBackend; +use self::backend::DrmBackendInternal; use self::error::*; static LOAD: Once = ONCE_INIT; /// Representation of an open drm device node to create rendering backends -pub struct DrmDevice> + 'static> { +pub struct DrmDevice { context: Rc, GbmDevice>>, - backends: HashMap>, old_state: HashMap)>, + device_id: dev_t, + backends: HashMap>>, active: bool, logger: ::slog::Logger, } -impl> + Borrow> + 'static> DrmDevice { +impl DrmDevice { /// Create a new `DrmDevice` from an open drm node /// /// Returns an error if the file is no valid drm node or context creation was not @@ -273,6 +280,8 @@ impl> + Borrow> ); }); + let device_id = fstat(dev.as_raw_fd()).chain_err(|| ErrorKind::UnableToGetDeviceId)?.st_rdev; + let mut drm = DrmDevice { // Open the gbm device from the drm device and create a context based on that context: Rc::new(EGLContext::new( @@ -283,15 +292,11 @@ impl> + Borrow> gbm }, attributes, - PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - alpha_bits: Some(8), - ..Default::default() - }, + Default::default(), log.clone(), ).map_err(Error::from)?), backends: HashMap::new(), + device_id, old_state: HashMap::new(), active: true, logger: log.clone(), @@ -330,12 +335,11 @@ impl> + Borrow> /// /// Errors if initialization fails or the mode is not available on all given /// connectors. - pub fn create_backend<'a, I, S>( - &mut self, state: S, crtc: crtc::Handle, mode: Mode, connectors: I - ) -> Result<&StateToken> + pub fn create_backend( + &mut self, crtc: crtc::Handle, mode: Mode, connectors: I + ) -> Result> where - I: Into>, - S: Into>, + I: Into> { if self.backends.contains_key(&crtc) { bail!(ErrorKind::CrtcAlreadyInUse(crtc)); @@ -388,33 +392,13 @@ impl> + Borrow> let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc))); let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?; - self.backends - .insert(crtc, state.into().insert(backend.into())); - - Ok(self.backends.get(&crtc).unwrap()) + self.backends.insert(crtc, backend.weak()); + Ok(backend) } - /// Get the current backend for a given crtc if any - pub fn backend_for_crtc(&self, crtc: &crtc::Handle) -> Option<&StateToken> { - self.backends.get(crtc) - } - - /// Get all belonging backends - pub fn current_backends(&self) -> Vec<&StateToken> { - self.backends.values().collect() - } - - /// Destroy the backend using a given crtc if any - /// - /// ## Panics - /// Panics if the backend is already borrowed from the state - pub fn destroy_backend<'a, S>(&mut self, state: S, crtc: &crtc::Handle) - where - S: Into>, - { - if let Some(token) = self.backends.remove(crtc) { - state.into().remove(token); - } + /// Returns an internal device id, that is unique per boot per system + pub fn device_id(&self) -> u64 { + self.device_id } } @@ -430,20 +414,43 @@ impl DevPath for A { } } +impl PartialEq for DrmDevice { + fn eq(&self, other: &DrmDevice) -> bool { + self.device_id == other.device_id + } +} +impl Eq for DrmDevice {} + +impl Hash for DrmDevice { + fn hash(&self, state: &mut H) { + self.device_id.hash(state); + } +} + // for users convinience and FdEventSource registering -impl> + 'static> AsRawFd for DrmDevice { +impl AsRawFd for DrmDevice { fn as_raw_fd(&self) -> RawFd { self.context.as_raw_fd() } } -impl> + 'static> BasicDevice for DrmDevice {} -impl> + 'static> ControlDevice for DrmDevice {} +impl BasicDevice for DrmDevice {} +impl ControlDevice for DrmDevice {} -impl> + 'static> Drop for DrmDevice { +impl EGLWaylandExtensions for DrmDevice { + fn bind_wl_display(&self, display: &Display) -> EGLResult { + self.context.bind_wl_display(display) + } + + fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> { + self.context.unbind_wl_display(display) + } +} + +impl Drop for DrmDevice { fn drop(&mut self) { if Rc::strong_count(&self.context) > 1 { - panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed"); + panic!("Pending DrmBackends. You need to free all backends before the DrmDevice gets destroyed"); } for (handle, (info, connectors)) in self.old_state.drain() { if let Err(err) = crtc::set( @@ -469,50 +476,32 @@ impl> + 'static> Drop for Dr } } -impl> + 'static> Hash for DrmDevice { - fn hash(&self, state: &mut H) { - self.as_raw_fd().hash(state) - } -} - /// Handler for drm node events /// /// See module-level documentation for its use -pub trait DrmHandler> + 'static> { - /// A `DrmBackend` has finished swapping buffers and new frame can now +pub trait DrmHandler { + /// The `DrmBackend` of crtc has finished swapping buffers and new frame can now /// (and should be immediately) be rendered. - /// - /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering, - /// check using `DrmBackend::is`. - /// - /// ## Panics - /// The device is already borrowed from the given `state`. Borrowing it again will panic - /// and is not necessary as it is already provided via the `device` parameter. - fn ready<'a, S: Into>>( - &mut self, state: S, device: &mut DrmDevice, backend: &StateToken, crtc: crtc::Handle, + fn ready( + &mut self, device: &mut DrmDevice, crtc: crtc::Handle, frame: u32, duration: Duration, ); /// The `DrmDevice` has thrown an error. /// /// The related backends are most likely *not* usable anymore and /// the whole stack has to be recreated.. - /// - /// ## Panics - /// The device is already borrowed from the given `state`. Borrowing it again will panic - /// and is not necessary as it is already provided via the `device` parameter. - fn error<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice, error: DrmError); + fn error(&mut self, device: &mut DrmDevice, error: DrmError); } /// Bind a `DrmDevice` to an `EventLoop`, /// /// This will cause it to recieve events and feed them into an `DrmHandler` -pub fn drm_device_bind( - evlh: &mut EventLoopHandle, device: StateToken>, handler: H -) -> IoResult>, H)>> +pub fn drm_device_bind( + evlh: &mut EventLoopHandle, device: StateToken>, handler: H +) -> IoResult>, H)>> where A: ControlDevice + 'static, - B: From> + Borrow> + 'static, - H: DrmHandler + 'static, + H: DrmHandler + 'static, { let fd = evlh.state().get(&device).as_raw_fd(); evlh.add_fd_event_source( @@ -523,11 +512,10 @@ where ) } -fn fd_event_source_implementation() -> FdEventSourceImpl<(StateToken>, H)> +fn fd_event_source_implementation() -> FdEventSourceImpl<(StateToken>, H)> where A: ControlDevice + 'static, - B: From> + Borrow> + 'static, - H: DrmHandler + 'static, + H: DrmHandler + 'static, { FdEventSourceImpl { ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| { @@ -541,44 +529,40 @@ where match events { Ok(events) => for event in events { if let crtc::Event::PageFlip(event) = event { - evlh.state().with_value(dev_token, |state, mut dev| { - if dev.active { - if let Some(backend_token) = dev.backend_for_crtc(&event.crtc).cloned() { - // we can now unlock the buffer - state.get(&backend_token).borrow().unlock_buffer(); - trace!(logger, "Handling event for backend {:?}", event.crtc); - // and then call the user to render the next frame - handler.ready( - state, - &mut dev, - &backend_token, - event.crtc, - event.frame, - event.duration, - ); - } + let dev = evlh.state().get_mut(dev_token); + if dev.active { + if let Some(backend) = dev.backends.get(&event.crtc).iter().flat_map(|x| x.upgrade()).next() { + // we can now unlock the buffer + backend.unlock_buffer(); + trace!(logger, "Handling event for backend {:?}", event.crtc); + // and then call the user to render the next frame + handler.ready( + dev, + event.crtc, + event.frame, + event.duration, + ); + } else { + dev.backends.remove(&event.crtc); } - }); + } } }, - Err(err) => evlh.state().with_value(dev_token, |state, mut dev| { - handler.error(state, &mut dev, err) - }), + Err(err) => handler.error(evlh.state().get_mut(dev_token), err), }; }, error: |evlh, &mut (ref mut dev_token, ref mut handler), _, error| { - evlh.state().with_value(dev_token, |state, mut dev| { - warn!(dev.logger, "DrmDevice errored: {}", error); - handler.error(state, &mut dev, error.into()); - }) + let mut dev = evlh.state().get_mut(dev_token); + warn!(dev.logger, "DrmDevice errored: {}", error); + handler.error(&mut dev, error.into()); }, } } #[cfg(feature = "backend_session")] -impl> + 'static> SessionObserver for StateToken> { +impl SessionObserver for StateToken> { fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { - let device: &mut DrmDevice = state.get_mut(self); + let device: &mut DrmDevice = state.get_mut(self); device.active = false; if let Err(err) = device.drop_master() { error!( @@ -598,18 +582,25 @@ impl> + 'static> SessionObse err ); } - for token in device.backends.values() { - let backend = state.get(token); - if let Err(err) = backend.borrow().swap_buffers() { - // TODO handle this better? - error!( - device.logger, - "Failed to activate crtc ({:?}) again. Error: {}", - backend.borrow().crtc(), - err - ); + let mut crtcs = Vec::new(); + for (crtc, backend) in device.backends.iter() { + if let Some(backend) = backend.upgrade() { + backend.unlock_buffer(); + if let Err(err) = backend.page_flip(None) { + error!( + device.logger, + "Failed to activate crtc ({:?}) again. Error: {}", + crtc, + err + ); + } + } else { + crtcs.push(*crtc); } } + for crtc in crtcs { + device.backends.remove(&crtc); + } }) } } diff --git a/src/backend/graphics/egl/mod.rs b/src/backend/graphics/egl/mod.rs index 3ed149f..06718cc 100644 --- a/src/backend/graphics/egl/mod.rs +++ b/src/backend/graphics/egl/mod.rs @@ -20,7 +20,7 @@ pub mod native; pub mod surface; pub use self::surface::EGLSurface; pub mod wayland; -pub use self::wayland::{EGLImages, BufferAccessError}; +pub use self::wayland::{EGLWaylandExtensions, EGLImages, BufferAccessError}; /// Error that can happen when swapping buffers. #[derive(Debug, Clone, PartialEq)] @@ -90,15 +90,6 @@ impl ::std::error::Error for EglExtensionNotSupportedError { } } -impl From for EglExtensionNotSupportedError { - fn from(error: error::Error) -> Self { - match *error.kind() { - error::ErrorKind::EglExtensionNotSupported(extensions) => EglExtensionNotSupportedError(extensions), - _ => panic!("Error not convertible"), - } - } -} - /// Describes the pixel format of the main framebuffer #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct PixelFormat { @@ -153,35 +144,4 @@ pub trait EGLGraphicsBackend: GraphicsBackend { /// Returns the pixel format of the main framebuffer of the context. fn get_pixel_format(&self) -> PixelFormat; - - /// Binds this EGL context to the given Wayland display. - /// - /// This will allow clients to utilize EGL to create hardware-accelerated - /// surfaces. The server will need to be able to handle egl-wl_buffers. - /// See the `wayland::drm` module. - /// - /// ## Errors - /// - /// This might return `WlExtensionNotSupported` if binding is not supported - /// by the EGL implementation. - /// - /// This might return `OtherEGLDisplayAlreadyBound` if called for the same - /// `Display` multiple times, as only one context may be bound at any given time. - fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>; - - /// Unbinds this EGL context from the given Wayland display. - /// - /// This will stop clients from using previously available extensions - /// to utilize hardware-accelerated surface via EGL. - /// - /// ## Errors - /// - /// This might return `WlExtensionNotSupported` if binding is not supported - /// by the EGL implementation. - /// - /// This might return `OtherEGLDisplayAlreadyBound` if called for the same - /// `Display` multiple times, as only one context may be bound at any given time. - fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>; - - fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result; } diff --git a/src/backend/graphics/egl/native.rs b/src/backend/graphics/egl/native.rs index bd24b4a..43dfe06 100644 --- a/src/backend/graphics/egl/native.rs +++ b/src/backend/graphics/egl/native.rs @@ -66,6 +66,7 @@ impl Backend for Wayland { } } +#[cfg(feature = "backend_winit")] pub struct XlibWindow(*const c_void); #[cfg(feature = "backend_winit")] pub enum X11 {} diff --git a/src/backend/graphics/egl/wayland.rs b/src/backend/graphics/egl/wayland.rs index ac57d46..b29671c 100644 --- a/src/backend/graphics/egl/wayland.rs +++ b/src/backend/graphics/egl/wayland.rs @@ -4,11 +4,13 @@ use backend::graphics::egl::ffi::egl::types::EGLImage; use nix::libc::{c_uint}; use std::rc::{Rc, Weak}; use std::fmt; -use wayland_server::{Display, Resource}; +use wayland_server::{Display, Resource, StateToken, StateProxy}; use wayland_server::protocol::wl_buffer::WlBuffer; /// Error that can occur when accessing an EGL buffer pub enum BufferAccessError { + /// The corresponding Context is not alive anymore + ContextLost, /// This buffer is not managed by the EGL buffer NotManaged(WlBuffer), /// Failed to create EGLImages from the buffer @@ -20,6 +22,7 @@ pub enum BufferAccessError { impl fmt::Debug for BufferAccessError { fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { match *self { + BufferAccessError::ContextLost => write!(formatter, "BufferAccessError::ContextLost"), BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"), BufferAccessError::EGLImageCreationFailed => write!(formatter, "BufferAccessError::EGLImageCreationFailed"), BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err), @@ -31,8 +34,7 @@ impl fmt::Display for BufferAccessError { fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { use ::std::error::Error; match *self { - BufferAccessError::NotManaged(_) => write!(formatter, "{}", self.description()), - BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()), + BufferAccessError::ContextLost | BufferAccessError::NotManaged(_) | BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()), BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter), } } @@ -41,6 +43,7 @@ impl fmt::Display for BufferAccessError { impl ::std::error::Error for BufferAccessError { fn description(&self) -> &str { match *self { + BufferAccessError::ContextLost => "The corresponding context was lost", BufferAccessError::NotManaged(_) => "This buffer is not mananged by EGL", BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer", BufferAccessError::EglExtensionNotSupported(ref err) => err.description(), @@ -173,7 +176,7 @@ impl Drop for EGLImages { } } -impl> EGLContext { +pub trait EGLWaylandExtensions { /// Binds this EGL context to the given Wayland display. /// /// This will allow clients to utilize EGL to create hardware-accelerated @@ -187,16 +190,7 @@ impl> EGLContext { /// /// This might return `OtherEGLDisplayAlreadyBound` if called for the same /// `Display` multiple times, as only one context may be bound at any given time. - pub fn bind_wl_display(&self, display: &Display) -> Result<()> { - if !self.wl_drm_support { - bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); - } - let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; - if res == 0 { - bail!(ErrorKind::OtherEGLDisplayAlreadyBound); - } - Ok(()) - } + fn bind_wl_display(&self, display: &Display) -> Result; /// Unbinds this EGL context from the given Wayland display. /// @@ -210,7 +204,112 @@ impl> EGLContext { /// /// This might return `OtherEGLDisplayAlreadyBound` if called for the same /// `Display` multiple times, as only one context may be bound at any given time. - pub fn unbind_wl_display(&self, display: &Display) -> Result<()> { + fn unbind_wl_display(&self, display: &Display) -> Result<()>; +} + +pub struct EGLDisplay(Weak); + +impl EGLDisplay { + pub fn new>(context: &EGLContext) -> EGLDisplay { + EGLDisplay(Rc::downgrade(&context.display)) + } + + pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result { + if let Some(display) = self.0.upgrade() { + let mut format: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } { + return Err(BufferAccessError::NotManaged(buffer)); + } + let format = match format { + x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, + x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA, + ffi::egl::TEXTURE_EXTERNAL_WL => Format::External, + ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV, + ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V, + ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV, + _ => panic!("EGL returned invalid texture type"), + }; + + let mut width: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } { + return Err(BufferAccessError::NotManaged(buffer)); + } + + let mut height: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } { + return Err(BufferAccessError::NotManaged(buffer)); + } + + let mut inverted: i32 = 0; + if unsafe { ffi::egl::QueryWaylandBufferWL(*display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) != 0 } { + inverted = 1; + } + + let mut images = Vec::with_capacity(format.num_planes()); + for i in 0..format.num_planes() { + let mut out = Vec::with_capacity(3); + out.push(ffi::egl::WAYLAND_PLANE_WL as i32); + out.push(i as i32); + out.push(ffi::egl::NONE as i32); + + images.push({ + let image = + unsafe { ffi::egl::CreateImageKHR( + *display, + ffi::egl::NO_CONTEXT, + ffi::egl::WAYLAND_BUFFER_WL, + buffer.ptr() as *mut _, + out.as_ptr(), + ) }; + if image == ffi::egl::NO_IMAGE_KHR { + return Err(BufferAccessError::EGLImageCreationFailed); + } else { + image + } + }); + } + + Ok(EGLImages { + display: Rc::downgrade(&display), + width: width as u32, + height: height as u32, + y_inverted: inverted != 0, + format, + images, + buffer, + }) + } else { + Err(BufferAccessError::ContextLost) + } + } +} + +impl EGLWaylandExtensions for Rc +{ + fn bind_wl_display(&self, display: &Display) -> Result { + (**self).bind_wl_display(display) + } + fn unbind_wl_display(&self, display: &Display) -> Result<()> { + (**self).unbind_wl_display(display) + } +} + +impl> EGLWaylandExtensions for EGLContext { + fn bind_wl_display(&self, display: &Display) -> Result { + if !self.wl_drm_support { + bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); + } + if !self.egl_to_texture_support { + bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"])); + } + let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) }; + if res == 0 { + bail!(ErrorKind::OtherEGLDisplayAlreadyBound); + } + Ok(EGLDisplay::new(self)) + } + + fn unbind_wl_display(&self, display: &Display) -> Result<()> { if !self.wl_drm_support { bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"])); } @@ -220,76 +319,4 @@ impl> EGLContext { } Ok(()) } - - pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result { - if !self.wl_drm_support { - return Err(EglExtensionNotSupportedError(&["EGL_WL_bind_wayland_display"]).into()); - } - if !self.egl_to_texture_support { - return Err(EglExtensionNotSupportedError(&["GL_OES_EGL_image"]).into()); - } - - let mut format: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged(buffer)); - } - let format = match format { - x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, - x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA, - ffi::egl::TEXTURE_EXTERNAL_WL => Format::External, - ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV, - ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V, - ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV, - _ => panic!("EGL returned invalid texture type"), - }; - - let mut width: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged(buffer)); - } - - let mut height: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } { - return Err(BufferAccessError::NotManaged(buffer)); - } - - let mut inverted: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) != 0 } { - inverted = 1; - } - - let mut images = Vec::with_capacity(format.num_planes()); - for i in 0..format.num_planes() { - let mut out = Vec::with_capacity(3); - out.push(ffi::egl::WAYLAND_PLANE_WL as i32); - out.push(i as i32); - out.push(ffi::egl::NONE as i32); - - images.push({ - let image = - unsafe { ffi::egl::CreateImageKHR( - *self.display, - ffi::egl::NO_CONTEXT, - ffi::egl::WAYLAND_BUFFER_WL, - buffer.ptr() as *mut _, - out.as_ptr(), - ) }; - if image == ffi::egl::NO_IMAGE_KHR { - return Err(BufferAccessError::EGLImageCreationFailed); - } else { - image - } - }); - } - - Ok(EGLImages { - display: Rc::downgrade(&self.display), - width: width as u32, - height: height as u32, - y_inverted: inverted != 0, - format, - images, - buffer, - }) - } } diff --git a/src/backend/graphics/glium.rs b/src/backend/graphics/glium.rs index 484b798..50856de 100644 --- a/src/backend/graphics/glium.rs +++ b/src/backend/graphics/glium.rs @@ -1,6 +1,8 @@ //! Glium compatibility module -use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError}; +use backend::graphics::egl::{EGLGraphicsBackend, SwapBuffersError, EglExtensionNotSupportedError}; +use backend::graphics::egl::wayland::{BufferAccessError, EGLImages, EGLWaylandExtensions, EGLDisplay}; +use backend::graphics::egl::error::Result as EGLResult; use glium::Frame; use glium::SwapBuffersError as GliumSwapBuffersError; use glium::backend::{Backend, Context, Facade}; @@ -8,6 +10,8 @@ use glium::debug::DebugCallbackBehavior; use std::borrow::Borrow; use std::os::raw::c_void; use std::rc::Rc; +use wayland_server::Display; +use wayland_server::protocol::wl_buffer::WlBuffer; impl From for GliumSwapBuffersError { fn from(error: SwapBuffersError) -> Self { @@ -73,6 +77,16 @@ impl From for GliumGraphicsBackend { } } +impl EGLWaylandExtensions for GliumGraphicsBackend { + fn bind_wl_display(&self, display: &Display) -> EGLResult { + (*self.backend).0.bind_wl_display(display) + } + fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> { + (*self.backend).0.unbind_wl_display(display) + } +} + + unsafe impl Backend for InternalBackend { fn swap_buffers(&self) -> Result<(), GliumSwapBuffersError> { self.0.swap_buffers().map_err(Into::into) diff --git a/src/backend/udev.rs b/src/backend/udev.rs index 7b2ab4e..99d996a 100644 --- a/src/backend/udev.rs +++ b/src/backend/udev.rs @@ -11,11 +11,10 @@ use drm::Device as BasicDevice; use drm::control::Device as ControlDevice; -use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler}; +use backend::drm::{drm_device_bind, DrmDevice, DrmHandler}; use backend::session::{Session, SessionObserver}; use nix::fcntl; use nix::sys::stat::{dev_t, fstat}; -use std::borrow::Borrow; use std::collections::HashMap; use std::ffi::OsString; use std::io::{Error as IoError, Result as IoResult}; @@ -42,16 +41,15 @@ impl ControlDevice for SessionFdDrmDevice {} /// given handler of any changes. Can be used to provide hot-plug functionality for gpus and /// attached monitors. pub struct UdevBackend< - B: Borrow> + 'static, - H: DrmHandler + 'static, + H: DrmHandler + 'static, S: Session + 'static, - T: UdevHandler + 'static, + T: UdevHandler + 'static, > { devices: HashMap< dev_t, ( - StateToken>, - FdEventSource<(StateToken>, H)>, + StateToken>, + FdEventSource<(StateToken>, H)>, ), >, monitor: MonitorSocket, @@ -61,11 +59,10 @@ pub struct UdevBackend< } impl< - B: From> + Borrow> + 'static, - H: DrmHandler + 'static, + H: DrmHandler + 'static, S: Session + 'static, - T: UdevHandler + 'static, -> UdevBackend { + T: UdevHandler + 'static, +> UdevBackend { /// Creates a new `UdevBackend` and adds it to the given `EventLoop`'s state. /// /// ## Arguments @@ -76,7 +73,7 @@ impl< /// `logger` - slog Logger to be used by the backend and its `DrmDevices`. pub fn new<'a, L>( mut evlh: &mut EventLoopHandle, context: &Context, mut session: S, mut handler: T, logger: L - ) -> Result>> + ) -> Result>> where L: Into>, { @@ -99,17 +96,17 @@ impl< }, logger.clone() ) { // Call the handler, which might add it to the runloop - Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) { - // fstat them - Some(drm_handler) => match fstat(device.as_raw_fd()) { - Ok(stat) => { + Ok(mut device) => { + let fd = device.as_raw_fd(); + let devnum = device.device_id(); + match handler.device_added(&mut evlh.state().as_proxy(), &mut device) { + Some(drm_handler) => { let token = evlh.state().insert(device); if let Ok(event_source) = drm_device_bind(&mut evlh, token.clone(), drm_handler) { - Some((stat.st_rdev, (token, event_source))) + Some((devnum, (token, event_source))) } else { handler.device_removed(evlh.state(), &token); let device = evlh.state().remove(token); - let fd = device.as_raw_fd(); drop(device); if let Err(err) = session.close(fd) { warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); @@ -117,27 +114,13 @@ impl< None } }, - Err(err) => { - // almost impossible to hit, but lets do it as good as possible - error!(logger, "Failed to get devnum of newly initialized device, dropping. Error: {:?}", err); - let token = evlh.state().insert(device); - handler.device_removed(evlh.state(), &token); - let device = evlh.state().remove(token); - let fd = device.as_raw_fd(); - drop(device); + None => { + drop(device); //drops master if let Err(err) = session.close(fd) { - warn!(logger, "Failed to close dropped device. Error: {:?}. Ignoring", err); - }; + warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); + } None } - }, - None => { - let fd = device.as_raw_fd(); - drop(device); //drops master - if let Err(err) = session.close(fd) { - warn!(logger, "Failed to close device. Error: {:?}. Ignoring", err); - } - None } }, Err(err) => { @@ -146,7 +129,7 @@ impl< } } }) - .collect::>, FdEventSource<(StateToken>, H)>)>>(); + .collect::>, FdEventSource<(StateToken>, H)>)>>(); let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?; builder @@ -193,11 +176,10 @@ impl< } impl< - B: Borrow> + 'static, - H: DrmHandler + 'static, + H: DrmHandler + 'static, S: Session + 'static, - T: UdevHandler + 'static, -> SessionObserver for StateToken> { + T: UdevHandler + 'static, +> SessionObserver for StateToken> { fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { state.with_value(self, |state, udev| { for &mut (ref mut device, _) in udev.devices.values_mut() { @@ -219,24 +201,22 @@ impl< /// /// Allows the backend to recieve kernel events and thus to drive the `UdevHandler`. /// No runtime functionality can be provided without using this function. -pub fn udev_backend_bind( - evlh: &mut EventLoopHandle, udev: StateToken> -) -> IoResult>>> +pub fn udev_backend_bind( + evlh: &mut EventLoopHandle, udev: StateToken> +) -> IoResult>>> where - B: From> + Borrow> + 'static, - H: DrmHandler + 'static, - T: UdevHandler + 'static, + H: DrmHandler + 'static, + T: UdevHandler + 'static, S: Session + 'static, { let fd = evlh.state().get(&udev).monitor.as_raw_fd(); evlh.add_fd_event_source(fd, fd_event_source_implementation(), udev, FdInterest::READ) } -fn fd_event_source_implementation() -> FdEventSourceImpl>> +fn fd_event_source_implementation() -> FdEventSourceImpl>> where - B: From> + Borrow> + 'static, - H: DrmHandler + 'static, - T: UdevHandler + 'static, + H: DrmHandler + 'static, + T: UdevHandler + 'static, S: Session + 'static, { FdEventSourceImpl { @@ -282,6 +262,7 @@ where } } }; + let fd = device.as_raw_fd(); match evlh.state().with_value(token, |state, udev| { udev.handler.device_added(state, &mut device) }) { @@ -299,7 +280,6 @@ where let mut state: StateProxy = state.into(); udev.handler.device_removed(&mut state, &dev_token); let device = state.remove(dev_token); - let fd = device.as_raw_fd(); drop(device); if let Err(err) = udev.session.close(fd) { warn!( @@ -312,7 +292,6 @@ where } } None => { - let fd = device.as_raw_fd(); drop(device); evlh.state().with_value(token, |_state, udev| { if let Err(err) = udev.session.close(fd) { @@ -375,7 +354,7 @@ where } /// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime. -pub trait UdevHandler> + 'static, H: DrmHandler + 'static> +pub trait UdevHandler + 'static> { /// Called on initialization for every known device and when a new device is detected. /// @@ -383,7 +362,7 @@ pub trait UdevHandler> + 'static, H: Dr /// /// ## Panics /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) + fn device_added<'a, S: Into>>(&mut self, state: S, device: &mut DrmDevice) -> Option; /// Called when an open device is changed. /// @@ -392,7 +371,7 @@ pub trait UdevHandler> + 'static, H: Dr /// /// ## Panics /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + fn device_changed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); /// Called when a device was removed. /// /// The device will not accept any operations anymore and its file descriptor will be closed once @@ -400,7 +379,7 @@ pub trait UdevHandler> + 'static, H: Dr /// /// ## Panics /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. - fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); + fn device_removed<'a, S: Into>>(&mut self, state: S, device: &StateToken>); /// Called when the udev context has encountered and error. /// /// ## Panics diff --git a/src/backend/winit.rs b/src/backend/winit.rs index 1dd4add..8c0d6e9 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,10 +1,12 @@ //! Implementation of backend traits for types provided by `winit` use backend::graphics::GraphicsBackend; -use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, BufferAccessError, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError, EGLImages}; +use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError}; use backend::graphics::egl::error as egl_error; +use backend::graphics::egl::error::Result as EGLResult; use backend::graphics::egl::native; -use backend::graphics::egl::context::{GlAttributes, PixelFormatRequirements}; +use backend::graphics::egl::context::GlAttributes; +use backend::graphics::egl::wayland::{EGLWaylandExtensions, EGLImages, Format, BufferAccessError, EGLDisplay}; use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, @@ -255,27 +257,20 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { Window::X11 { ref context, .. } => context.get_pixel_format(), } } +} - fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> { +impl EGLWaylandExtensions for WinitGraphicsBackend { + fn bind_wl_display(&self, display: &Display) -> EGLResult { match *self.window { Window::Wayland { ref context, .. } => context.bind_wl_display(display), Window::X11 { ref context, .. } => context.bind_wl_display(display), - }?; - Ok(()) + } } - fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> { + fn unbind_wl_display(&self, display: &Display) -> EGLResult<()> { match *self.window { Window::Wayland { ref context, .. } => context.unbind_wl_display(display), Window::X11 { ref context, .. } => context.unbind_wl_display(display), - }?; - Ok(()) - } - - fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result { - match *self.window { - Window::Wayland { ref context, .. } => context.egl_buffer_contents(buffer), - Window::X11 { ref context, .. } => context.egl_buffer_contents(buffer), } } }