drm: Make surfaces `Send`

This commit is contained in:
Victor Brekenfeld 2020-06-05 22:57:52 +02:00
parent 68f3c0642d
commit 7b4459f649
23 changed files with 492 additions and 382 deletions

View File

@ -15,7 +15,7 @@ bitflags = "1"
calloop = "0.6.2" calloop = "0.6.2"
dbus = { version = "0.8", optional = true } dbus = { version = "0.8", optional = true }
drm = { version = "^0.4.0", git = "https://github.com/drakulix/drm-rs", branch = "develop", optional = true } drm = { version = "^0.4.0", git = "https://github.com/drakulix/drm-rs", branch = "develop", optional = true }
gbm = { version = "^0.6.0", git = "https://github.com/drakulix/gbm.rs", branch = "develop", optional = true, default-features = false, features = ["drm-support"] } gbm = { version = "^0.6.0", git = "https://github.com/drakulix/gbm.rs", branch = "thread-safe", optional = true, default-features = false, features = ["drm-support"] }
glium = { version = "0.27.0", optional = true, default-features = false } glium = { version = "0.27.0", optional = true, default-features = false }
image = { version = "0.23.0", optional = true } image = { version = "0.23.0", optional = true }
input = { version = "0.5", default-features = false, optional = true } input = { version = "0.5", default-features = false, optional = true }

View File

@ -17,13 +17,13 @@ use smithay::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend};
use smithay::{ use smithay::{
backend::{ backend::{
drm::{ drm::{
atomic::AtomicDrmDevice, atomic::{AtomicDrmDevice, AtomicDrmSurface},
common::fallback::{FallbackDevice, FallbackSurface}, common::fallback::{FallbackDevice, FallbackSurface},
device_bind, device_bind,
egl::{EglDevice, EglSurface}, egl::{EglDevice, EglSurface},
eglstream::{egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface}, eglstream::{egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface},
gbm::{egl::Gbm as EglGbmBackend, GbmDevice, GbmSurface}, gbm::{egl::Gbm as EglGbmBackend, GbmDevice, GbmSurface},
legacy::LegacyDrmDevice, legacy::{LegacyDrmDevice, LegacyDrmSurface},
DevPath, Device, DeviceHandler, Surface, DevPath, Device, DeviceHandler, Surface,
}, },
graphics::{CursorBackend, SwapBuffersError}, graphics::{CursorBackend, SwapBuffersError},
@ -86,8 +86,8 @@ type RenderDevice = FallbackDevice<
>, >,
>; >;
type RenderSurface = FallbackSurface< type RenderSurface = FallbackSurface<
EglSurface<GbmSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>, EglSurface<GbmSurface<FallbackSurface<AtomicDrmSurface<SessionFd>, LegacyDrmSurface<SessionFd>>>>,
EglSurface<EglStreamSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>, EglSurface<EglStreamSurface<FallbackSurface<AtomicDrmSurface<SessionFd>, LegacyDrmSurface<SessionFd>>>>,
>; >;
pub fn run_udev( pub fn run_udev(

View File

@ -8,15 +8,15 @@ use slog::Drain;
use smithay::{ use smithay::{
backend::{ backend::{
drm::{ drm::{
atomic::AtomicDrmDevice, atomic::{AtomicDrmDevice, AtomicDrmSurface},
common::fallback::{EitherError, FallbackDevice}, common::fallback::{EitherError, FallbackDevice, FallbackSurface},
common::Error as DrmError, common::Error as DrmError,
device_bind, device_bind,
egl::{EglDevice, EglSurface, Error as EglError}, egl::{EglDevice, EglSurface, Error as EglError},
eglstream::{ eglstream::{
egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface, Error as EglStreamError, egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface, Error as EglStreamError,
}, },
legacy::LegacyDrmDevice, legacy::{LegacyDrmDevice, LegacyDrmSurface},
Device, DeviceHandler, Device, DeviceHandler,
}, },
graphics::glium::GliumGraphicsBackend, graphics::glium::GliumGraphicsBackend,
@ -145,7 +145,7 @@ pub struct DrmHandlerImpl {
GliumGraphicsBackend< GliumGraphicsBackend<
EglSurface< EglSurface<
EglStreamSurface< EglStreamSurface<
FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>, FallbackSurface<AtomicDrmSurface<ClonableFile>, LegacyDrmSurface<ClonableFile>>,
>, >,
>, >,
>, >,

View File

@ -21,10 +21,10 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::Rc;
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc, Weak,
}; };
use drm::control::{atomic::AtomicModeReq, AtomicCommitFlags, Device as ControlDevice, Event}; use drm::control::{atomic::AtomicModeReq, AtomicCommitFlags, Device as ControlDevice, Event};
@ -49,7 +49,7 @@ pub mod session;
/// Open raw drm device utilizing atomic mode-setting /// Open raw drm device utilizing atomic mode-setting
pub struct AtomicDrmDevice<A: AsRawFd + 'static> { pub struct AtomicDrmDevice<A: AsRawFd + 'static> {
dev: Rc<Dev<A>>, dev: Arc<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>,
@ -360,7 +360,7 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
} }
Ok(AtomicDrmDevice { Ok(AtomicDrmDevice {
dev: Rc::new(dev), dev: Arc::new(dev),
dev_id, dev_id,
active, active,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
@ -414,7 +414,7 @@ impl<A: AsRawFd + 'static> Device for AtomicDrmDevice<A> {
return Err(Error::SurfaceWithoutConnectors(crtc)); return Err(Error::SurfaceWithoutConnectors(crtc));
} }
let backend = Rc::new(AtomicDrmSurfaceInternal::new( let backend = Arc::new(AtomicDrmSurfaceInternal::new(
self.dev.clone(), self.dev.clone(),
crtc, crtc,
mode, mode,
@ -422,7 +422,7 @@ impl<A: AsRawFd + 'static> Device for AtomicDrmDevice<A> {
self.logger.new(o!("crtc" => format!("{:?}", crtc))), self.logger.new(o!("crtc" => format!("{:?}", crtc))),
)?); )?);
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend)); self.backends.borrow_mut().insert(crtc, Arc::downgrade(&backend));
Ok(AtomicDrmSurface(backend)) Ok(AtomicDrmSurface(backend))
} }

View File

@ -13,9 +13,9 @@ use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::{Arc, Weak as WeakArc};
use super::{AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev}; use super::{surface::CursorState, AtomicDrmDevice, AtomicDrmSurfaceInternal, Dev};
use crate::backend::drm::{common::Error, DevPath, Surface}; use crate::backend::drm::{common::Error, DevPath, Surface};
use crate::{ use crate::{
backend::session::Signal as SessionSignal, backend::session::Signal as SessionSignal,
@ -26,18 +26,18 @@ use crate::{
/// linked to the [`AtomicDrmDevice`](AtomicDrmDevice) /// linked to the [`AtomicDrmDevice`](AtomicDrmDevice)
/// it was created from. /// it was created from.
pub struct AtomicDrmDeviceObserver<A: AsRawFd + 'static> { pub struct AtomicDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>, dev: WeakArc<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
privileged: bool, privileged: bool,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<AtomicDrmSurfaceInternal<A>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, WeakArc<AtomicDrmSurfaceInternal<A>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<A: AsRawFd + 'static> Linkable<SessionSignal> for AtomicDrmDevice<A> { impl<A: AsRawFd + 'static> Linkable<SessionSignal> for AtomicDrmDevice<A> {
fn link(&mut self, signaler: Signaler<SessionSignal>) { fn link(&mut self, signaler: Signaler<SessionSignal>) {
let mut observer = AtomicDrmDeviceObserver { let mut observer = AtomicDrmDeviceObserver {
dev: Rc::downgrade(&self.dev), dev: Arc::downgrade(&self.dev),
dev_id: self.dev_id, dev_id: self.dev_id,
active: self.active.clone(), active: self.active.clone(),
privileged: self.dev.privileged, privileged: self.dev.privileged,
@ -72,7 +72,7 @@ impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
// TODO: Clear overlay planes (if we ever use them) // TODO: Clear overlay planes (if we ever use them)
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) { for surface in backends.borrow().values().filter_map(WeakArc::upgrade) {
// other ttys that use no cursor, might not clear it themselves. // other ttys that use no cursor, might not clear it themselves.
// This makes sure our cursor won't stay visible. // This makes sure our cursor won't stay visible.
// //
@ -187,7 +187,7 @@ impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
// //
// Lets do that, by creating a garbage/non-matching current-state. // Lets do that, by creating a garbage/non-matching current-state.
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) { for surface in backends.borrow().values().filter_map(WeakArc::upgrade) {
let mut current = surface.state.write().unwrap(); let mut current = surface.state.write().unwrap();
// lets force a non matching state // lets force a non matching state
@ -202,9 +202,11 @@ impl<A: AsRawFd + 'static> AtomicDrmDeviceObserver<A> {
surface.use_mode(mode)?; surface.use_mode(mode)?;
// drop cursor state to force setting the cursor again. // drop cursor state to force setting the cursor again.
surface.cursor.position.set(None); *surface.cursor.lock().unwrap() = CursorState {
surface.cursor.hotspot.set((0, 0)); position: None,
surface.cursor.framebuffer.set(None); hotspot: (0, 0),
framebuffer: None,
};
} }
} }
} }

View File

@ -6,11 +6,9 @@ use drm::control::{
}; };
use drm::Device as BasicDevice; use drm::Device as BasicDevice;
use std::cell::Cell;
use std::collections::HashSet; use std::collections::HashSet;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc; use std::sync::{atomic::Ordering, Arc, Mutex, RwLock};
use std::sync::{atomic::Ordering, RwLock};
use failure::ResultExt as FailureResultExt; use failure::ResultExt as FailureResultExt;
@ -20,9 +18,9 @@ use crate::backend::graphics::CursorBackend;
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct CursorState { pub struct CursorState {
pub position: Cell<Option<(u32, u32)>>, pub position: Option<(u32, u32)>,
pub hotspot: Cell<(u32, u32)>, pub hotspot: (u32, u32),
pub framebuffer: Cell<Option<framebuffer::Handle>>, pub framebuffer: Option<framebuffer::Handle>,
} }
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
@ -39,14 +37,14 @@ pub struct Planes {
} }
pub(in crate::backend::drm) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> { pub(in crate::backend::drm) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> {
pub(super) dev: Rc<Dev<A>>, pub(super) dev: Arc<Dev<A>>,
pub(in crate::backend::drm) crtc: crtc::Handle, pub(in crate::backend::drm) crtc: crtc::Handle,
pub(super) cursor: CursorState, pub(super) cursor: Mutex<CursorState>,
pub(in crate::backend::drm) planes: Planes, pub(in crate::backend::drm) planes: Planes,
pub(super) state: RwLock<State>, pub(super) state: RwLock<State>,
pub(super) pending: RwLock<State>, pub(super) pending: RwLock<State>,
pub(super) logger: ::slog::Logger, pub(super) logger: ::slog::Logger,
pub(super) test_buffer: Cell<Option<(DumbBuffer, framebuffer::Handle)>>, pub(super) test_buffer: Mutex<Option<(DumbBuffer, framebuffer::Handle)>>,
} }
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurfaceInternal<A> {
@ -60,7 +58,7 @@ impl<A: AsRawFd + 'static> ControlDevice for AtomicDrmSurfaceInternal<A> {}
impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
pub(crate) fn new( pub(crate) fn new(
dev: Rc<Dev<A>>, dev: Arc<Dev<A>>,
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: &[connector::Handle], connectors: &[connector::Handle],
@ -167,16 +165,16 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
let surface = AtomicDrmSurfaceInternal { let surface = AtomicDrmSurfaceInternal {
dev, dev,
crtc, crtc,
cursor: CursorState { cursor: Mutex::new(CursorState {
position: Cell::new(None), position: None,
framebuffer: Cell::new(None), framebuffer: None,
hotspot: Cell::new((0, 0)), hotspot: (0, 0),
}, }),
planes: Planes { primary, cursor }, planes: Planes { primary, cursor },
state: RwLock::new(state), state: RwLock::new(state),
pending: RwLock::new(pending), pending: RwLock::new(pending),
logger, logger,
test_buffer: Cell::new(None), test_buffer: Mutex::new(None),
}; };
Ok(surface) Ok(surface)
@ -202,10 +200,13 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
dev: self.dev_path(), dev: self.dev_path(),
source, source,
})?; })?;
if let Some((old_db, old_fb)) = self.test_buffer.replace(Some((db, fb))) {
let mut test_buffer = self.test_buffer.lock().unwrap();
if let Some((old_db, old_fb)) = test_buffer.take() {
let _ = self.destroy_framebuffer(old_fb); let _ = self.destroy_framebuffer(old_fb);
let _ = self.destroy_dumb_buffer(old_db); let _ = self.destroy_dumb_buffer(old_db);
}; };
*test_buffer = Some((db, fb));
Ok(fb) Ok(fb)
} }
@ -213,7 +214,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
impl<A: AsRawFd + 'static> Drop for AtomicDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> Drop for AtomicDrmSurfaceInternal<A> {
fn drop(&mut self) { fn drop(&mut self) {
if let Some((db, fb)) = self.test_buffer.take() { if let Some((db, fb)) = self.test_buffer.lock().unwrap().take() {
let _ = self.destroy_framebuffer(fb); let _ = self.destroy_framebuffer(fb);
let _ = self.destroy_dumb_buffer(db); let _ = self.destroy_dumb_buffer(db);
} }
@ -613,7 +614,7 @@ impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurfaceInternal<A> {
} }
trace!(self.logger, "New cursor position ({},{}) pending", x, y); trace!(self.logger, "New cursor position ({},{}) pending", x, y);
self.cursor.position.set(Some((x, y))); self.cursor.lock().unwrap().position = Some((x, y));
Ok(()) Ok(())
} }
@ -628,21 +629,20 @@ impl<A: AsRawFd + 'static> CursorBackend for AtomicDrmSurfaceInternal<A> {
trace!(self.logger, "Setting the new imported cursor"); trace!(self.logger, "Setting the new imported cursor");
if let Some(fb) = self.cursor.framebuffer.get().take() { let mut cursor = self.cursor.lock().unwrap();
if let Some(fb) = cursor.framebuffer.take() {
let _ = self.destroy_framebuffer(fb); let _ = self.destroy_framebuffer(fb);
} }
self.cursor.framebuffer.set(Some( cursor.framebuffer = Some(self.add_planar_framebuffer(buffer, &[0; 4], 0).compat().map_err(
self.add_planar_framebuffer(buffer, &[0; 4], 0) |source| Error::Access {
.compat() errmsg: "Failed to import cursor",
.map_err(|source| Error::Access { dev: self.dev_path(),
errmsg: "Failed to import cursor", source,
dev: self.dev_path(), },
source, )?);
})?, cursor.hotspot = hotspot;
));
self.cursor.hotspot.set(hotspot);
Ok(()) Ok(())
} }
@ -828,19 +828,17 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
); );
} }
let cursor_pos = self.cursor.position.get();
let cursor_fb = self.cursor.framebuffer.get();
// if there is a cursor, we add the cursor plane to the request as well. // if there is a cursor, we add the cursor plane to the request as well.
// this synchronizes cursor movement with rendering, which reduces flickering. // this synchronizes cursor movement with rendering, which reduces flickering.
if let (Some(pos), Some(fb)) = (cursor_pos, cursor_fb) { let mut cursor = self.cursor.lock().unwrap();
if let (Some(pos), Some(fb)) = (cursor.position, cursor.framebuffer) {
match self.get_framebuffer(fb).compat().map_err(|source| Error::Access { match self.get_framebuffer(fb).compat().map_err(|source| Error::Access {
errmsg: "Error getting cursor fb", errmsg: "Error getting cursor fb",
dev: self.dev_path(), dev: self.dev_path(),
source, source,
}) { }) {
Ok(cursor_info) => { Ok(cursor_info) => {
let hotspot = self.cursor.hotspot.get(); let hotspot = cursor.hotspot;
// again like the primary plane we need to set crtc and framebuffer. // again like the primary plane we need to set crtc and framebuffer.
req.add_property( req.add_property(
@ -898,7 +896,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
} }
Err(err) => { Err(err) => {
warn!(self.logger, "Cursor FB invalid: {}. Skipping.", err); warn!(self.logger, "Cursor FB invalid: {}. Skipping.", err);
self.cursor.framebuffer.set(None); cursor.framebuffer = None;
} }
} }
} }
@ -997,7 +995,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
/// Open raw crtc utilizing atomic mode-setting /// Open raw crtc utilizing atomic mode-setting
pub struct AtomicDrmSurface<A: AsRawFd + 'static>( pub struct AtomicDrmSurface<A: AsRawFd + 'static>(
pub(in crate::backend::drm) Rc<AtomicDrmSurfaceInternal<A>>, pub(in crate::backend::drm) Arc<AtomicDrmSurfaceInternal<A>>,
); );
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurface<A> { impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurface<A> {
@ -1080,3 +1078,16 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurface<A> {
RawSurface::page_flip(&*self.0, framebuffer) RawSurface::page_flip(&*self.0, framebuffer)
} }
} }
#[cfg(test)]
mod test {
use super::AtomicDrmSurface;
use std::fs::File;
fn is_send<S: Send>() {}
#[test]
fn surface_is_send() {
is_send::<AtomicDrmSurface<File>>();
}
}

View File

@ -16,7 +16,8 @@ use nix::libc::dev_t;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::Rc;
use std::sync::{Arc, Weak as WeakArc};
#[cfg(feature = "use_system_lib")] #[cfg(feature = "use_system_lib")]
use wayland_server::Display; use wayland_server::Display;
@ -49,7 +50,7 @@ pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'sta
} }
pub(crate) type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>); pub(crate) type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
type BackendRef<D> = Weak<EglSurfaceInternal<<D as Device>::Surface>>; type BackendRef<D> = WeakArc<EglSurfaceInternal<<D as Device>::Surface>>;
/// Representation of an egl device to create egl rendering surfaces /// Representation of an egl device to create egl rendering surfaces
pub struct EglDevice<B, D> pub struct EglDevice<B, D>
@ -214,8 +215,8 @@ where
SurfaceCreationError::NativeSurfaceCreationFailed(err) => Error::Underlying(err), SurfaceCreationError::NativeSurfaceCreationFailed(err) => Error::Underlying(err),
})?; })?;
let backend = Rc::new(EglSurfaceInternal { context, surface }); let backend = Arc::new(EglSurfaceInternal { context, surface });
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend)); self.backends.borrow_mut().insert(crtc, Arc::downgrade(&backend));
Ok(EglSurface(backend)) Ok(EglSurface(backend))
} }

View File

@ -7,6 +7,7 @@ use drm::control::{connector, crtc, Mode};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::{atomic::Ordering, Weak as WeakArc};
use super::{EglDevice, EglSurfaceInternal}; use super::{EglDevice, EglSurfaceInternal};
use crate::backend::drm::{Device, Surface}; use crate::backend::drm::{Device, Surface};
@ -23,7 +24,7 @@ use crate::{
/// linked to the [`EglDevice`](EglDevice) it was /// linked to the [`EglDevice`](EglDevice) it was
/// created from. /// created from.
pub struct EglDeviceObserver<N: NativeSurface + Surface> { pub struct EglDeviceObserver<N: NativeSurface + Surface> {
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglSurfaceInternal<N>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, WeakArc<EglSurfaceInternal<N>>>>>,
} }
impl<B, D> Linkable<SessionSignal> for EglDevice<B, D> impl<B, D> Linkable<SessionSignal> for EglDevice<B, D>
@ -63,7 +64,10 @@ impl<N: NativeSurface + Surface> EglDeviceObserver<N> {
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_crtc, backend) in backends.borrow().iter() { for (_crtc, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
let old_surface = backend.surface.surface.replace(std::ptr::null()); let old_surface = backend
.surface
.surface
.swap(std::ptr::null_mut(), Ordering::SeqCst);
if !old_surface.is_null() { if !old_surface.is_null() {
unsafe { unsafe {
ffi::egl::DestroySurface(**backend.surface.display, old_surface as *const _); ffi::egl::DestroySurface(**backend.surface.display, old_surface as *const _);

View File

@ -1,6 +1,7 @@
use drm::control::{connector, crtc, Mode}; use drm::control::{connector, crtc, Mode};
use nix::libc::c_void; use nix::libc::c_void;
use std::convert::TryInto; use std::convert::TryInto;
use std::sync::Arc;
use super::Error; use super::Error;
use crate::backend::drm::Surface; use crate::backend::drm::Surface;
@ -12,10 +13,8 @@ use crate::backend::graphics::gl::GLGraphicsBackend;
use crate::backend::graphics::PixelFormat; use crate::backend::graphics::PixelFormat;
use crate::backend::graphics::{CursorBackend, SwapBuffersError}; use crate::backend::graphics::{CursorBackend, SwapBuffersError};
use std::rc::Rc;
/// Egl surface for rendering /// Egl surface for rendering
pub struct EglSurface<N: native::NativeSurface + Surface>(pub(super) Rc<EglSurfaceInternal<N>>); pub struct EglSurface<N: native::NativeSurface + Surface>(pub(super) Arc<EglSurfaceInternal<N>>);
pub(super) struct EglSurfaceInternal<N> pub(super) struct EglSurfaceInternal<N>
where where
@ -136,3 +135,18 @@ where
self.0.surface.get_pixel_format() self.0.surface.get_pixel_format()
} }
} }
#[cfg(test)]
mod test {
use super::EglSurface;
use crate::backend::drm::gbm::GbmSurface;
use crate::backend::drm::legacy::LegacyDrmSurface;
use std::fs::File;
fn is_send<S: Send>() {}
#[test]
fn surface_is_send() {
is_send::<EglSurface<GbmSurface<LegacyDrmSurface<File>>>>();
}
}

View File

@ -5,13 +5,13 @@
//! //!
#[cfg(feature = "backend_drm_atomic")] #[cfg(feature = "backend_drm_atomic")]
use crate::backend::drm::atomic::AtomicDrmDevice; use crate::backend::drm::atomic::AtomicDrmSurface;
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] #[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
use crate::backend::drm::common::fallback::{EitherError, FallbackDevice, FallbackSurface}; use crate::backend::drm::common::fallback::{EitherError, FallbackSurface};
#[cfg(any(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] #[cfg(any(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
use crate::backend::drm::common::Error as DrmError; use crate::backend::drm::common::Error as DrmError;
#[cfg(feature = "backend_drm_legacy")] #[cfg(feature = "backend_drm_legacy")]
use crate::backend::drm::legacy::LegacyDrmDevice; use crate::backend::drm::legacy::LegacyDrmSurface;
use crate::backend::drm::{Device, RawDevice, RawSurface, Surface}; use crate::backend::drm::{Device, RawDevice, RawSurface, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::{ use crate::backend::egl::{
@ -37,9 +37,10 @@ pub struct EglStreamDeviceBackend<D: RawDevice + 'static> {
impl<D: RawDevice + 'static> Backend for EglStreamDeviceBackend<D> impl<D: RawDevice + 'static> Backend for EglStreamDeviceBackend<D>
where where
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>, EglStreamSurface<<D as Device>::Surface>:
NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
{ {
type Surface = EglStreamSurface<D>; type Surface = EglStreamSurface<<D as Device>::Surface>;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<<D as Device>::Surface as Surface>::Error>;
// create an EGLDisplay for the EGLstream platform // create an EGLDisplay for the EGLstream platform
@ -73,7 +74,8 @@ where
unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<EglStreamDeviceBackend<D>> unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<EglStreamDeviceBackend<D>>
for EglStreamDevice<D> for EglStreamDevice<D>
where where
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>, EglStreamSurface<<D as Device>::Surface>:
NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
{ {
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>); type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
@ -100,7 +102,8 @@ where
fn create_surface( fn create_surface(
&mut self, &mut self,
args: Self::Arguments, args: Self::Arguments,
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<EglStreamSurface<<D as Device>::Surface>, Error<<<D as Device>::Surface as Surface>::Error>>
{
Device::create_surface(self, args.0, args.1, &args.2) Device::create_surface(self, args.0, args.1, &args.2)
} }
} }
@ -111,7 +114,7 @@ where
// as a result, we need three implemenations for atomic, legacy and fallback... // as a result, we need three implemenations for atomic, legacy and fallback...
#[cfg(feature = "backend_drm_atomic")] #[cfg(feature = "backend_drm_atomic")]
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<AtomicDrmDevice<A>> { unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<AtomicDrmSurface<A>> {
type Error = Error<DrmError>; type Error = Error<DrmError>;
unsafe fn create( unsafe fn create(
@ -141,17 +144,12 @@ unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<AtomicDrmDe
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface, surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<DrmError>>> { ) -> Result<(), SwapBuffersError<Error<DrmError>>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
self.flip(self.0.crtc.0.crtc, display, surface) self.flip(self.0.crtc.0.crtc, display, surface)
} }
} }
#[cfg(feature = "backend_drm_legacy")] #[cfg(feature = "backend_drm_legacy")]
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<LegacyDrmDevice<A>> { unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<LegacyDrmSurface<A>> {
type Error = Error<DrmError>; type Error = Error<DrmError>;
unsafe fn create( unsafe fn create(
@ -181,17 +179,13 @@ unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<LegacyDrmDe
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface, surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<DrmError>>> { ) -> Result<(), SwapBuffersError<Error<DrmError>>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
self.flip(self.0.crtc.0.crtc, display, surface) self.flip(self.0.crtc.0.crtc, display, surface)
} }
} }
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] #[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
unsafe impl<A: AsRawFd + 'static> NativeSurface unsafe impl<A: AsRawFd + 'static> NativeSurface
for EglStreamSurface<FallbackDevice<AtomicDrmDevice<A>, LegacyDrmDevice<A>>> for EglStreamSurface<FallbackSurface<AtomicDrmSurface<A>, LegacyDrmSurface<A>>>
{ {
type Error = Error<EitherError<DrmError, DrmError>>; type Error = Error<EitherError<DrmError, DrmError>>;
@ -230,10 +224,6 @@ unsafe impl<A: AsRawFd + 'static> NativeSurface
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface, surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Self::Error>> { ) -> Result<(), SwapBuffersError<Self::Error>> {
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
}
let crtc = match &self.0.crtc { let crtc = match &self.0.crtc {
FallbackSurface::Preference(dev) => dev.0.crtc, FallbackSurface::Preference(dev) => dev.0.crtc,
FallbackSurface::Fallback(dev) => dev.0.crtc, FallbackSurface::Fallback(dev) => dev.0.crtc,

View File

@ -25,12 +25,13 @@ use drm::SystemError as DrmError;
use failure::ResultExt; use failure::ResultExt;
use nix::libc::dev_t; use nix::libc::dev_t;
use std::cell::{Cell, RefCell}; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CStr; use std::ffi::CStr;
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::MetadataExt;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::{Arc, Mutex, Weak as WeakArc};
use std::{fs, ptr}; use std::{fs, ptr};
mod surface; mod surface;
@ -78,11 +79,12 @@ pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'sta
StreamFlipFailed(#[source] RawEGLError), StreamFlipFailed(#[source] RawEGLError),
} }
type SurfaceInternalRef<D> = WeakArc<EglStreamSurfaceInternal<<D as Device>::Surface>>;
/// Representation of an open egl stream device to create egl rendering surfaces /// Representation of an open egl stream device to create egl rendering surfaces
pub struct EglStreamDevice<D: RawDevice + ControlDevice + 'static> { pub struct EglStreamDevice<D: RawDevice + ControlDevice + 'static> {
pub(self) dev: EGLDeviceEXT, pub(self) dev: EGLDeviceEXT,
raw: D, raw: D,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, SurfaceInternalRef<D>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>, links: Vec<crate::signaling::SignalToken>,
@ -227,7 +229,7 @@ impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> {
struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> { struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> {
handler: Box<dyn DeviceHandler<Device = EglStreamDevice<D>> + 'static>, handler: Box<dyn DeviceHandler<Device = EglStreamDevice<D>> + 'static>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, SurfaceInternalRef<D>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -254,7 +256,7 @@ impl<D: RawDevice + ControlDevice + 'static> DeviceHandler for InternalDeviceHan
} }
impl<D: RawDevice + ControlDevice + 'static> Device for EglStreamDevice<D> { impl<D: RawDevice + ControlDevice + 'static> Device for EglStreamDevice<D> {
type Surface = EglStreamSurface<D>; type Surface = EglStreamSurface<<D as Device>::Surface>;
fn device_id(&self) -> dev_t { fn device_id(&self) -> dev_t {
self.raw.device_id() self.raw.device_id()
@ -277,30 +279,31 @@ impl<D: RawDevice + ControlDevice + 'static> Device for EglStreamDevice<D> {
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: &[connector::Handle], connectors: &[connector::Handle],
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<EglStreamSurface<<D as Device>::Surface>, Error<<<D as Device>::Surface as Surface>::Error>>
{
info!(self.logger, "Initializing EglStreamSurface"); info!(self.logger, "Initializing EglStreamSurface");
let drm_surface = let drm_surface =
Device::create_surface(&mut self.raw, crtc, mode, connectors).map_err(Error::Underlying)?; Device::create_surface(&mut self.raw, crtc, mode, connectors).map_err(Error::Underlying)?;
// initialize a buffer for the cursor image // initialize a buffer for the cursor image
let cursor = Cell::new(Some(( let cursor = Some((
self.raw self.raw
.create_dumb_buffer((1, 1), PixelFormat::ARGB8888) .create_dumb_buffer((1, 1), PixelFormat::ARGB8888)
.compat() .compat()
.map_err(Error::BufferCreationFailed)?, .map_err(Error::BufferCreationFailed)?,
(0, 0), (0, 0),
))); ));
let backend = Rc::new(EglStreamSurfaceInternal { let backend = Arc::new(EglStreamSurfaceInternal {
crtc: drm_surface, crtc: drm_surface,
cursor, cursor: Mutex::new(cursor),
stream: RefCell::new(None), stream: Mutex::new(None),
commit_buffer: Cell::new(None), commit_buffer: Mutex::new(None),
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))), logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
locked: std::sync::atomic::AtomicBool::new(false), locked: std::sync::atomic::AtomicBool::new(false),
}); });
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend)); self.backends.borrow_mut().insert(crtc, Arc::downgrade(&backend));
Ok(EglStreamSurface(backend)) Ok(EglStreamSurface(backend))
} }

View File

@ -4,7 +4,7 @@
//! //!
use super::{EglStreamDevice, EglStreamSurfaceInternal}; use super::{EglStreamDevice, EglStreamSurfaceInternal};
use crate::backend::drm::{RawDevice, Surface}; use crate::backend::drm::{Device, RawDevice, RawSurface};
use crate::backend::egl::ffi; use crate::backend::egl::ffi;
use crate::backend::session::Signal as SessionSignal; use crate::backend::session::Signal as SessionSignal;
use crate::signaling::{Linkable, Signaler}; use crate::signaling::{Linkable, Signaler};
@ -12,14 +12,15 @@ use crate::signaling::{Linkable, Signaler};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::Weak as WeakArc;
use drm::control::{crtc, Device as ControlDevice}; use drm::control::{crtc, Device as ControlDevice};
/// [`SessionObserver`](SessionObserver) /// [`SessionObserver`](SessionObserver)
/// linked to the [`EglStreamDevice`](EglStreamDevice) it was /// linked to the [`EglStreamDevice`](EglStreamDevice) it was
/// created from. /// created from.
pub struct EglStreamDeviceObserver<D: RawDevice + 'static> { pub struct EglStreamDeviceObserver<S: RawSurface + 'static> {
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, WeakArc<EglStreamSurfaceInternal<S>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -30,7 +31,7 @@ where
fn link(&mut self, signaler: Signaler<SessionSignal>) { fn link(&mut self, signaler: Signaler<SessionSignal>) {
let lower_signal = Signaler::new(); let lower_signal = Signaler::new();
self.raw.link(lower_signal.clone()); self.raw.link(lower_signal.clone());
let mut observer = EglStreamDeviceObserver { let mut observer = EglStreamDeviceObserver::<<D as Device>::Surface> {
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), logger: self.logger.clone(),
}; };
@ -52,19 +53,19 @@ where
} }
} }
impl<D: RawDevice + 'static> EglStreamDeviceObserver<D> { impl<S: RawSurface + 'static> EglStreamDeviceObserver<S> {
fn pause(&mut self) { fn pause(&mut self) {
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_, backend) in backends.borrow().iter() { for (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
// destroy/disable the streams so it will not submit any pending frames // destroy/disable the streams so it will not submit any pending frames
if let Some((display, stream)) = backend.stream.replace(None) { if let Some((display, stream)) = backend.stream.lock().unwrap().take() {
unsafe { unsafe {
ffi::egl::DestroyStreamKHR(display.handle, stream); ffi::egl::DestroyStreamKHR(display.handle, stream.0);
} }
} }
// framebuffers will be likely not valid anymore, lets just recreate those after activation. // framebuffers will be likely not valid anymore, lets just recreate those after activation.
if let Some((buffer, fb)) = backend.commit_buffer.take() { if let Some((buffer, fb)) = backend.commit_buffer.lock().unwrap().take() {
let _ = backend.crtc.destroy_framebuffer(fb); let _ = backend.crtc.destroy_framebuffer(fb);
let _ = backend.crtc.destroy_dumb_buffer(buffer); let _ = backend.crtc.destroy_dumb_buffer(buffer);
} }
@ -77,17 +78,18 @@ impl<D: RawDevice + 'static> EglStreamDeviceObserver<D> {
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for (_, backend) in backends.borrow().iter() { for (_, backend) in backends.borrow().iter() {
if let Some(backend) = backend.upgrade() { if let Some(backend) = backend.upgrade() {
if let Some((cursor, hotspot)) = backend.cursor.get() { let cursor = backend.cursor.lock().unwrap();
if let Some((ref cursor, ref hotspot)) = &*cursor {
if backend if backend
.crtc .crtc
.set_cursor2( .set_cursor2(
backend.crtc.crtc(), backend.crtc.crtc(),
Some(&cursor), Some(cursor),
(hotspot.0 as i32, hotspot.1 as i32), (hotspot.0 as i32, hotspot.1 as i32),
) )
.is_err() .is_err()
{ {
if let Err(err) = backend.crtc.set_cursor(backend.crtc.crtc(), Some(&cursor)) { if let Err(err) = backend.crtc.set_cursor(backend.crtc.crtc(), Some(cursor)) {
warn!(self.logger, "Failed to reset cursor: {}", err) warn!(self.logger, "Failed to reset cursor: {}", err)
} }
} }

View File

@ -1,21 +1,19 @@
use super::super::{Device, RawDevice, RawSurface, Surface}; use super::super::{RawSurface, Surface};
use super::Error; use super::Error;
use drm::buffer::format::PixelFormat; use drm::buffer::format::PixelFormat;
use drm::control::{connector, crtc, dumbbuffer::DumbBuffer, framebuffer, Device as ControlDevice, Mode}; use drm::control::{connector, crtc, dumbbuffer::DumbBuffer, framebuffer, Mode};
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
use failure::ResultExt; use failure::ResultExt;
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
use nix::libc::{c_int, c_void}; use nix::libc::{c_int, c_void};
use std::cell::{Cell, RefCell};
use std::ffi::CStr; use std::ffi::CStr;
use std::ptr; use std::ptr;
use std::rc::Rc;
use std::sync::{ use std::sync::{
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
Arc, Arc, Mutex,
}; };
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
@ -30,18 +28,24 @@ use crate::backend::egl::{display::EGLDisplayHandle, wrap_egl_call, EGLError, Sw
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
pub(in crate::backend::drm) struct EglStreamSurfaceInternal<D: RawDevice + 'static> { // We do not want to mark the whole surface as `Send` in case we screw up somewhere else
pub(in crate::backend::drm) crtc: <D as Device>::Surface, // and because S needs to be `Send` as well for this to work.
pub(in crate::backend::drm) cursor: Cell<Option<(DumbBuffer, (u32, u32))>>, pub(super) struct StreamHandle(pub(super) EGLStreamKHR);
pub(in crate::backend::drm) stream: RefCell<Option<(Arc<EGLDisplayHandle>, EGLStreamKHR)>>, // EGLStreamKHR can be moved between threads
pub(in crate::backend::drm) commit_buffer: Cell<Option<(DumbBuffer, framebuffer::Handle)>>, unsafe impl Send for StreamHandle {}
pub(in crate::backend::drm) struct EglStreamSurfaceInternal<S: RawSurface + 'static> {
pub(in crate::backend::drm) crtc: S,
pub(in crate::backend::drm) cursor: Mutex<Option<(DumbBuffer, (u32, u32))>>,
pub(super) stream: Mutex<Option<(Arc<EGLDisplayHandle>, StreamHandle)>>,
pub(in crate::backend::drm) commit_buffer: Mutex<Option<(DumbBuffer, framebuffer::Handle)>>,
pub(in crate::backend::drm) locked: AtomicBool, pub(in crate::backend::drm) locked: AtomicBool,
pub(in crate::backend::drm) logger: ::slog::Logger, pub(in crate::backend::drm) logger: ::slog::Logger,
} }
impl<D: RawDevice + 'static> Surface for EglStreamSurfaceInternal<D> { impl<S: RawSurface + 'static> Surface for EglStreamSurfaceInternal<S> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors; type Connectors = <S as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<S as Surface>::Error>;
fn crtc(&self) -> crtc::Handle { fn crtc(&self) -> crtc::Handle {
self.crtc.crtc() self.crtc.crtc()
@ -75,23 +79,23 @@ impl<D: RawDevice + 'static> Surface for EglStreamSurfaceInternal<D> {
self.crtc.pending_mode() self.crtc.pending_mode()
} }
fn use_mode(&self, mode: Mode) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> { fn use_mode(&self, mode: Mode) -> Result<(), Error<<S as Surface>::Error>> {
self.crtc.use_mode(mode).map_err(Error::Underlying) self.crtc.use_mode(mode).map_err(Error::Underlying)
} }
} }
impl<D: RawDevice + 'static> Drop for EglStreamSurfaceInternal<D> { impl<S: RawSurface + 'static> Drop for EglStreamSurfaceInternal<S> {
fn drop(&mut self) { fn drop(&mut self) {
if let Some((buffer, _)) = self.cursor.get() { if let Some((buffer, _)) = self.cursor.lock().unwrap().take() {
let _ = self.crtc.destroy_dumb_buffer(buffer); let _ = self.crtc.destroy_dumb_buffer(buffer);
} }
if let Some((buffer, fb)) = self.commit_buffer.take() { if let Some((buffer, fb)) = self.commit_buffer.lock().unwrap().take() {
let _ = self.crtc.destroy_framebuffer(fb); let _ = self.crtc.destroy_framebuffer(fb);
let _ = self.crtc.destroy_dumb_buffer(buffer); let _ = self.crtc.destroy_dumb_buffer(buffer);
} }
if let Some((display, stream)) = self.stream.replace(None) { if let Some((display, stream)) = self.stream.lock().unwrap().take() {
unsafe { unsafe {
egl::DestroyStreamKHR(display.handle, stream); egl::DestroyStreamKHR(display.handle, stream.0);
} }
} }
} }
@ -110,7 +114,7 @@ impl<D: RawDevice + 'static> Drop for EglStreamSurfaceInternal<D> {
// Note that this might still appear a little choppy, we should just use software cursors // Note that this might still appear a little choppy, we should just use software cursors
// on eglstream devices by default and only use this, if the user really wants it. // on eglstream devices by default and only use this, if the user really wants it.
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurfaceInternal<D> { impl<S: RawSurface + 'static> CursorBackend for EglStreamSurfaceInternal<S> {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<DrmError>; type Error = Error<DrmError>;
@ -178,7 +182,7 @@ impl<D: RawDevice + 'static> CursorBackend for EglStreamSurfaceInternal<D> {
} }
// and store it // and store it
if let Some((old, _)) = self.cursor.replace(Some((cursor, hotspot))) { if let Some((old, _)) = self.cursor.lock().unwrap().replace((cursor, hotspot)) {
if self.crtc.destroy_dumb_buffer(old).is_err() { if self.crtc.destroy_dumb_buffer(old).is_err() {
warn!(self.logger, "Failed to free old cursor"); warn!(self.logger, "Failed to free old cursor");
} }
@ -189,14 +193,14 @@ impl<D: RawDevice + 'static> CursorBackend for EglStreamSurfaceInternal<D> {
} }
/// egl stream surface for rendering /// egl stream surface for rendering
pub struct EglStreamSurface<D: RawDevice + 'static>( pub struct EglStreamSurface<S: RawSurface + 'static>(
pub(in crate::backend::drm) Rc<EglStreamSurfaceInternal<D>>, pub(in crate::backend::drm) Arc<EglStreamSurfaceInternal<S>>,
); );
impl<D: RawDevice + 'static> EglStreamSurface<D> { impl<S: RawSurface + 'static> EglStreamSurface<S> {
/// Check if underlying gbm resources need to be recreated. /// Check if underlying gbm resources need to be recreated.
pub fn needs_recreation(&self) -> bool { pub fn needs_recreation(&self) -> bool {
self.0.crtc.commit_pending() || self.0.stream.borrow().is_none() self.0.crtc.commit_pending() || self.0.stream.lock().unwrap().is_none()
} }
// An EGLStream is basically the pump that requests and consumes images to display them. // An EGLStream is basically the pump that requests and consumes images to display them.
@ -205,12 +209,13 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
&self, &self,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
output_attribs: &[isize], output_attribs: &[isize],
) -> Result<EGLStreamKHR, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<EGLStreamKHR, Error<<S as Surface>::Error>> {
let mut stream = self.0.stream.lock().unwrap();
// drop old steam, if it already exists // drop old steam, if it already exists
if let Some((display, stream)) = self.0.stream.replace(None) { if let Some((display, stream)) = stream.take() {
// ignore result // ignore result
unsafe { unsafe {
ffi::egl::DestroyStreamKHR(display.handle, stream); ffi::egl::DestroyStreamKHR(display.handle, stream.0);
} }
} }
@ -225,7 +230,7 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
.create_dumb_buffer((w as u32, h as u32), PixelFormat::ARGB8888) .create_dumb_buffer((w as u32, h as u32), PixelFormat::ARGB8888)
{ {
if let Ok(fb) = self.0.crtc.add_framebuffer(&buffer) { if let Ok(fb) = self.0.crtc.add_framebuffer(&buffer) {
if let Some((buffer, fb)) = self.0.commit_buffer.replace(Some((buffer, fb))) { if let Some((buffer, fb)) = self.0.commit_buffer.lock().unwrap().replace((buffer, fb)) {
let _ = self.0.crtc.destroy_framebuffer(fb); let _ = self.0.crtc.destroy_framebuffer(fb);
let _ = self.0.crtc.destroy_dumb_buffer(buffer); let _ = self.0.crtc.destroy_dumb_buffer(buffer);
} }
@ -388,21 +393,21 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
}; };
// okay, we have a config, lets create the stream. // okay, we have a config, lets create the stream.
let stream = unsafe { ffi::egl::CreateStreamKHR(display.handle, stream_attributes.as_ptr()) }; let raw_stream = unsafe { ffi::egl::CreateStreamKHR(display.handle, stream_attributes.as_ptr()) };
if stream == ffi::egl::NO_STREAM_KHR { if raw_stream == ffi::egl::NO_STREAM_KHR {
error!(self.0.logger, "Failed to create egl stream"); error!(self.0.logger, "Failed to create egl stream");
return Err(Error::DeviceStreamCreationFailed); return Err(Error::DeviceStreamCreationFailed);
} }
// we have a stream, lets connect it to our output layer // we have a stream, lets connect it to our output layer
if unsafe { ffi::egl::StreamConsumerOutputEXT(display.handle, stream, layer) } == 0 { if unsafe { ffi::egl::StreamConsumerOutputEXT(display.handle, raw_stream, layer) } == 0 {
error!(self.0.logger, "Failed to link Output Layer as Stream Consumer"); error!(self.0.logger, "Failed to link Output Layer as Stream Consumer");
return Err(Error::DeviceStreamCreationFailed); return Err(Error::DeviceStreamCreationFailed);
} }
let _ = self.0.stream.replace(Some((display.clone(), stream))); *stream = Some((display.clone(), StreamHandle(raw_stream)));
Ok(stream) Ok(raw_stream)
} }
pub(super) fn create_surface( pub(super) fn create_surface(
@ -411,7 +416,7 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
_surface_attribs: &[c_int], _surface_attribs: &[c_int],
output_attribs: &[isize], output_attribs: &[isize],
) -> Result<*const c_void, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<*const c_void, Error<<S as Surface>::Error>> {
// our surface needs a stream // our surface needs a stream
let stream = self.create_stream(display, output_attribs)?; let stream = self.create_stream(display, output_attribs)?;
@ -450,7 +455,7 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
crtc: crtc::Handle, crtc: crtc::Handle,
display: &Arc<EGLDisplayHandle>, display: &Arc<EGLDisplayHandle>,
surface: ffi::egl::types::EGLSurface, surface: ffi::egl::types::EGLSurface,
) -> Result<(), SwapBuffersError<Error<<<D as Device>::Surface as Surface>::Error>>> { ) -> Result<(), SwapBuffersError<Error<<S as Surface>::Error>>> {
// if we have already swapped the buffer successfully, we need to free it again. // if we have already swapped the buffer successfully, we need to free it again.
// //
// we need to do this here, because the call may fail (compare this with gbm's unlock_buffer...). // we need to do this here, because the call may fail (compare this with gbm's unlock_buffer...).
@ -466,7 +471,7 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
ffi::egl::NONE as isize, ffi::egl::NONE as isize,
]; ];
if let Ok(stream) = self.0.stream.try_borrow() { if let Ok(stream) = self.0.stream.try_lock() {
// lets try to acquire the frame. // lets try to acquire the frame.
// this may fail, if the buffer is still in use by the gpu, // this may fail, if the buffer is still in use by the gpu,
// e.g. the flip was not done yet. In this case this call fails as `BUSY`. // e.g. the flip was not done yet. In this case this call fails as `BUSY`.
@ -474,7 +479,7 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
wrap_egl_call(|| unsafe { wrap_egl_call(|| unsafe {
ffi::egl::StreamConsumerAcquireAttribNV( ffi::egl::StreamConsumerAcquireAttribNV(
display.handle, display.handle,
*stream, stream.0,
acquire_attributes.as_ptr(), acquire_attributes.as_ptr(),
); );
}) })
@ -503,9 +508,9 @@ impl<D: RawDevice + 'static> EglStreamSurface<D> {
} }
} }
impl<D: RawDevice + 'static> Surface for EglStreamSurface<D> { impl<S: RawSurface + 'static> Surface for EglStreamSurface<S> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors; type Connectors = <S as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<S as Surface>::Error>;
fn crtc(&self) -> crtc::Handle { fn crtc(&self) -> crtc::Handle {
self.0.crtc() self.0.crtc()
@ -545,7 +550,7 @@ impl<D: RawDevice + 'static> Surface for EglStreamSurface<D> {
} }
#[cfg(feature = "backend_drm_legacy")] #[cfg(feature = "backend_drm_legacy")]
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurface<D> { impl<S: RawSurface + 'static> CursorBackend for EglStreamSurface<S> {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<DrmError>; type Error = Error<DrmError>;
@ -561,3 +566,17 @@ impl<D: RawDevice + 'static> CursorBackend for EglStreamSurface<D> {
self.0.set_cursor_representation(buffer, hotspot) self.0.set_cursor_representation(buffer, hotspot)
} }
} }
#[cfg(test)]
mod test {
use super::EglStreamSurface;
use crate::backend::drm::legacy::LegacyDrmSurface;
use std::fs::File;
fn is_send<S: Send>() {}
#[test]
fn surface_is_send() {
is_send::<EglStreamSurface<LegacyDrmSurface<File>>>();
}
}

View File

@ -4,7 +4,7 @@
//! [`GbmDevice`](GbmDevice) and [`GbmSurface`](GbmSurface). //! [`GbmDevice`](GbmDevice) and [`GbmSurface`](GbmSurface).
//! //!
use crate::backend::drm::{Device, RawDevice, Surface}; use crate::backend::drm::{Device, RawDevice, RawSurface, Surface};
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
use crate::backend::egl::{display::EGLDisplayHandle, ffi}; use crate::backend::egl::{display::EGLDisplayHandle, ffi};
use crate::backend::egl::{ use crate::backend::egl::{
@ -27,7 +27,7 @@ pub struct Gbm<D: RawDevice + 'static> {
} }
impl<D: RawDevice + 'static> Backend for Gbm<D> { impl<D: RawDevice + 'static> Backend for Gbm<D> {
type Surface = GbmSurface<D>; type Surface = GbmSurface<<D as Device>::Surface>;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<<D as Device>::Surface as Surface>::Error>;
// this creates an EGLDisplay for the gbm platform. // this creates an EGLDisplay for the gbm platform.
@ -75,19 +75,19 @@ unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for Gb
} }
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> { fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> {
Ok(self.dev.borrow().as_raw() as *const _) Ok(self.dev.lock().unwrap().as_raw() as *const _)
} }
fn create_surface( fn create_surface(
&mut self, &mut self,
args: Self::Arguments, args: Self::Arguments,
) -> Result<GbmSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<GbmSurface<<D as Device>::Surface>, Error<<<D as Device>::Surface as Surface>::Error>> {
Device::create_surface(self, args.0, args.1, &args.2) Device::create_surface(self, args.0, args.1, &args.2)
} }
} }
unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> { unsafe impl<S: RawSurface + 'static> NativeSurface for GbmSurface<S> {
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<S as Surface>::Error>;
unsafe fn create( unsafe fn create(
&self, &self,
@ -101,7 +101,7 @@ unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
ffi::egl::CreateWindowSurface( ffi::egl::CreateWindowSurface(
display.handle, display.handle,
config_id, config_id,
self.0.surface.borrow().as_raw() as *const _, self.0.surface.lock().unwrap().as_raw() as *const _,
surface_attributes.as_ptr(), surface_attributes.as_ptr(),
) )
}) })

View File

@ -16,15 +16,14 @@ use crate::backend::graphics::SwapBuffersError;
use drm::control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode}; use drm::control::{connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode};
use drm::SystemError as DrmError; use drm::SystemError as DrmError;
use gbm::{self, BufferObjectFlags, Format as GbmFormat};
use nix::libc::dev_t; use nix::libc::dev_t;
use std::cell::{Cell, RefCell}; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::io; use std::io;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::Once; use std::sync::{Arc, Mutex, Once, Weak as WeakArc};
/// Errors thrown by the [`GbmDevice`](::backend::drm::gbm::GbmDevice) /// Errors thrown by the [`GbmDevice`](::backend::drm::gbm::GbmDevice)
/// and [`GbmSurface`](::backend::drm::gbm::GbmSurface). /// and [`GbmSurface`](::backend::drm::gbm::GbmSurface).
@ -74,10 +73,12 @@ pub mod session;
static LOAD: Once = Once::new(); static LOAD: Once = Once::new();
type SurfaceInternalRef<D> = WeakArc<GbmSurfaceInternal<<D as Device>::Surface>>;
/// Representation of an open gbm device to create rendering surfaces /// Representation of an open gbm device to create rendering surfaces
pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> { pub struct GbmDevice<D: RawDevice + ControlDevice + 'static> {
pub(self) dev: Rc<RefCell<gbm::Device<D>>>, pub(self) dev: Arc<Mutex<gbm::Device<gbm::FdWrapper>>>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>, pub(self) raw: D,
backends: Rc<RefCell<HashMap<crtc::Handle, SurfaceInternalRef<D>>>>,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
links: Vec<crate::signaling::SignalToken>, links: Vec<crate::signaling::SignalToken>,
logger: ::slog::Logger, logger: ::slog::Logger,
@ -112,7 +113,10 @@ impl<D: RawDevice + ControlDevice + 'static> GbmDevice<D> {
debug!(log, "Creating gbm device"); debug!(log, "Creating gbm device");
Ok(GbmDevice { Ok(GbmDevice {
// Open the gbm device from the drm device // Open the gbm device from the drm device
dev: Rc::new(RefCell::new(gbm::Device::new(dev).map_err(Error::InitFailed)?)), dev: Arc::new(Mutex::new(unsafe {
gbm::Device::new_from_fd(dev.as_raw_fd()).map_err(Error::InitFailed)?
})),
raw: dev,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
links: Vec::new(), links: Vec::new(),
@ -123,7 +127,7 @@ impl<D: RawDevice + ControlDevice + 'static> GbmDevice<D> {
struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> { struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> {
handler: Box<dyn DeviceHandler<Device = GbmDevice<D>> + 'static>, handler: Box<dyn DeviceHandler<Device = GbmDevice<D>> + 'static>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, SurfaceInternalRef<D>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -153,14 +157,14 @@ impl<D: RawDevice + ControlDevice + 'static> DeviceHandler for InternalDeviceHan
} }
impl<D: RawDevice + ControlDevice + 'static> Device for GbmDevice<D> { impl<D: RawDevice + ControlDevice + 'static> Device for GbmDevice<D> {
type Surface = GbmSurface<D>; type Surface = GbmSurface<<D as Device>::Surface>;
fn device_id(&self) -> dev_t { fn device_id(&self) -> dev_t {
self.dev.borrow().device_id() self.raw.device_id()
} }
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) { fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
self.dev.borrow_mut().set_handler(InternalDeviceHandler { self.raw.set_handler(InternalDeviceHandler {
handler: Box::new(handler), handler: Box::new(handler),
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), logger: self.logger.clone(),
@ -168,7 +172,7 @@ impl<D: RawDevice + ControlDevice + 'static> Device for GbmDevice<D> {
} }
fn clear_handler(&mut self) { fn clear_handler(&mut self) {
self.dev.borrow_mut().clear_handler(); self.raw.clear_handler();
} }
fn create_surface( fn create_surface(
@ -176,85 +180,54 @@ impl<D: RawDevice + ControlDevice + 'static> Device for GbmDevice<D> {
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: &[connector::Handle], connectors: &[connector::Handle],
) -> Result<GbmSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> { ) -> Result<GbmSurface<<D as Device>::Surface>, Error<<<D as Device>::Surface as Surface>::Error>> {
info!(self.logger, "Initializing GbmSurface"); info!(self.logger, "Initializing GbmSurface");
let drm_surface = Device::create_surface(&mut **self.dev.borrow_mut(), crtc, mode, connectors) let drm_surface = self
.raw
.create_surface(crtc, mode, connectors)
.map_err(Error::Underlying)?; .map_err(Error::Underlying)?;
// initialize the surface let backend = Arc::new(GbmSurfaceInternal::new(
let (w, h) = drm_surface.pending_mode().size(); self.dev.clone(),
let surface = self drm_surface,
.dev self.logger.new(o!("crtc" => format!("{:?}", crtc))),
.borrow() )?);
.create_surface( self.backends.borrow_mut().insert(crtc, Arc::downgrade(&backend));
w as u32,
h as u32,
GbmFormat::XRGB8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.map_err(Error::SurfaceCreationFailed)?;
// initialize a buffer for the cursor image
let cursor = Cell::new((
self.dev
.borrow()
.create_buffer_object(
1,
1,
GbmFormat::ARGB8888,
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
)
.map_err(Error::BufferCreationFailed)?,
(0, 0),
));
let backend = Rc::new(GbmSurfaceInternal {
dev: self.dev.clone(),
surface: RefCell::new(surface),
crtc: drm_surface,
cursor,
current_frame_buffer: Cell::new(None),
front_buffer: Cell::new(None),
next_buffer: Cell::new(None),
recreated: Cell::new(true),
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
});
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
Ok(GbmSurface(backend)) Ok(GbmSurface(backend))
} }
fn process_events(&mut self) { fn process_events(&mut self) {
self.dev.borrow_mut().process_events() self.raw.process_events()
} }
fn resource_handles(&self) -> Result<ResourceHandles, Error<<<D as Device>::Surface as Surface>::Error>> { fn resource_handles(&self) -> Result<ResourceHandles, Error<<<D as Device>::Surface as Surface>::Error>> {
Device::resource_handles(&**self.dev.borrow()).map_err(Error::Underlying) Device::resource_handles(&self.raw).map_err(Error::Underlying)
} }
fn get_connector_info(&self, conn: connector::Handle) -> std::result::Result<connector::Info, DrmError> { fn get_connector_info(&self, conn: connector::Handle) -> std::result::Result<connector::Info, DrmError> {
self.dev.borrow().get_connector_info(conn) self.raw.get_connector_info(conn)
} }
fn get_crtc_info(&self, crtc: crtc::Handle) -> std::result::Result<crtc::Info, DrmError> { fn get_crtc_info(&self, crtc: crtc::Handle) -> std::result::Result<crtc::Info, DrmError> {
self.dev.borrow().get_crtc_info(crtc) self.raw.get_crtc_info(crtc)
} }
fn get_encoder_info(&self, enc: encoder::Handle) -> std::result::Result<encoder::Info, DrmError> { fn get_encoder_info(&self, enc: encoder::Handle) -> std::result::Result<encoder::Info, DrmError> {
self.dev.borrow().get_encoder_info(enc) self.raw.get_encoder_info(enc)
} }
fn get_framebuffer_info( fn get_framebuffer_info(
&self, &self,
fb: framebuffer::Handle, fb: framebuffer::Handle,
) -> std::result::Result<framebuffer::Info, DrmError> { ) -> std::result::Result<framebuffer::Info, DrmError> {
self.dev.borrow().get_framebuffer_info(fb) self.raw.get_framebuffer_info(fb)
} }
fn get_plane_info(&self, plane: plane::Handle) -> std::result::Result<plane::Info, DrmError> { fn get_plane_info(&self, plane: plane::Handle) -> std::result::Result<plane::Info, DrmError> {
self.dev.borrow().get_plane_info(plane) self.raw.get_plane_info(plane)
} }
} }
impl<D: RawDevice + ControlDevice + 'static> AsRawFd for GbmDevice<D> { impl<D: RawDevice + ControlDevice + 'static> AsRawFd for GbmDevice<D> {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.dev.borrow().as_raw_fd() self.raw.as_raw_fd()
} }
} }

View File

@ -4,12 +4,11 @@
//! //!
use drm::control::crtc; use drm::control::crtc;
use gbm::BufferObject;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use super::{GbmDevice, GbmSurfaceInternal}; use super::{GbmDevice, SurfaceInternalRef};
use crate::backend::drm::{Device, RawDevice}; use crate::backend::drm::{Device, RawDevice};
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
use crate::{ use crate::{
@ -21,8 +20,8 @@ use crate::{
/// linked to the [`GbmDevice`](GbmDevice) it was /// linked to the [`GbmDevice`](GbmDevice) it was
/// created from. /// created from.
pub(crate) struct GbmDeviceObserver<D: RawDevice + ::drm::control::Device + 'static> { pub(crate) struct GbmDeviceObserver<D: RawDevice + ::drm::control::Device + 'static> {
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<GbmSurfaceInternal<D>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, SurfaceInternalRef<D>>>>,
logger: ::slog::Logger, _logger: ::slog::Logger,
} }
impl<D> Linkable<SessionSignal> for GbmDevice<D> impl<D> Linkable<SessionSignal> for GbmDevice<D>
@ -32,10 +31,10 @@ where
{ {
fn link(&mut self, signaler: Signaler<SessionSignal>) { fn link(&mut self, signaler: Signaler<SessionSignal>) {
let lower_signal = Signaler::new(); let lower_signal = Signaler::new();
self.dev.borrow_mut().link(lower_signal.clone()); self.raw.link(lower_signal.clone());
let mut observer = GbmDeviceObserver { let mut observer = GbmDeviceObserver::<D> {
backends: Rc::downgrade(&self.backends), backends: Rc::downgrade(&self.backends),
logger: self.logger.clone(), _logger: self.logger.clone(),
}; };
let token = signaler.register(move |&signal| match signal { let token = signaler.register(move |&signal| match signal {
@ -66,15 +65,8 @@ where
// reset cursor // reset cursor
{ {
use ::drm::control::Device; let cursor = backend.cursor.lock().unwrap();
let _ = backend.crtc.set_cursor_representation(&cursor.0, cursor.1);
let &(ref cursor, ref hotspot): &(BufferObject<()>, (u32, u32)) =
unsafe { &*backend.cursor.as_ptr() };
if backend.crtc.set_cursor_representation(cursor, *hotspot).is_err() {
if let Err(err) = backend.dev.borrow().set_cursor(*crtc, Some(cursor)) {
error!(self.logger, "Failed to reset cursor. Error: {}", err);
}
}
} }
} else { } else {
crtcs.push(*crtc); crtcs.push(*crtc);

View File

@ -1,99 +1,155 @@
use super::super::{Device, RawDevice, RawSurface, Surface}; use super::super::{RawSurface, Surface};
use super::Error; use super::Error;
use drm::control::{connector, crtc, framebuffer, Device as ControlDevice, Mode}; use drm::control::{connector, crtc, framebuffer, Mode};
use failure::ResultExt; use failure::ResultExt;
use gbm::{self, BufferObject, BufferObjectFlags, Format as GbmFormat, SurfaceBufferHandle}; use gbm::{self, BufferObject, BufferObjectFlags, Format as GbmFormat};
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
use std::cell::{Cell, RefCell}; use std::sync::{
use std::rc::Rc; atomic::{AtomicBool, Ordering},
Arc, Mutex,
};
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
pub(super) struct GbmSurfaceInternal<D: RawDevice + 'static> { pub struct Buffers {
pub(super) dev: Rc<RefCell<gbm::Device<D>>>, pub(super) current_frame_buffer: Option<framebuffer::Handle>,
pub(super) surface: RefCell<gbm::Surface<framebuffer::Handle>>, pub(super) front_buffer: Option<BufferObject<framebuffer::Handle>>,
pub(super) crtc: <D as Device>::Surface, pub(super) next_buffer: Option<BufferObject<framebuffer::Handle>>,
pub(super) cursor: Cell<(BufferObject<()>, (u32, u32))>, }
pub(super) current_frame_buffer: Cell<Option<framebuffer::Handle>>,
pub(super) front_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Handle>>>, pub(super) struct GbmSurfaceInternal<S: RawSurface + 'static> {
pub(super) next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Handle>>>, pub(super) dev: Arc<Mutex<gbm::Device<gbm::FdWrapper>>>,
pub(super) recreated: Cell<bool>, pub(super) surface: Mutex<gbm::Surface<framebuffer::Handle>>,
pub(super) crtc: S,
pub(super) cursor: Mutex<(BufferObject<()>, (u32, u32))>,
pub(super) buffers: Mutex<Buffers>,
pub(super) recreated: AtomicBool,
pub(super) logger: ::slog::Logger, pub(super) logger: ::slog::Logger,
} }
impl<D: RawDevice + 'static> GbmSurfaceInternal<D> { impl<S: RawSurface + 'static> GbmSurfaceInternal<S> {
pub(super) fn new(
dev: Arc<Mutex<gbm::Device<gbm::FdWrapper>>>,
drm_surface: S,
logger: ::slog::Logger,
) -> Result<GbmSurfaceInternal<S>, Error<<S as Surface>::Error>> {
// initialize the surface
let (w, h) = drm_surface.pending_mode().size();
let surface = dev
.lock()
.unwrap()
.create_surface(
w as u32,
h as u32,
GbmFormat::XRGB8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.map_err(Error::SurfaceCreationFailed)?;
// initialize a buffer for the cursor image
let cursor = (
dev.lock()
.unwrap()
.create_buffer_object(
1,
1,
GbmFormat::ARGB8888,
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
)
.map_err(Error::BufferCreationFailed)?,
(0, 0),
);
Ok(GbmSurfaceInternal {
dev,
surface: Mutex::new(surface),
crtc: drm_surface,
cursor: Mutex::new(cursor),
buffers: Mutex::new(Buffers {
current_frame_buffer: None,
front_buffer: None,
next_buffer: None,
}),
recreated: AtomicBool::new(true),
logger,
})
}
pub(super) fn unlock_buffer(&self) { pub(super) fn unlock_buffer(&self) {
// after the page swap is finished we need to release the rendered buffer. // after the page swap is finished we need to release the rendered buffer.
// this is called from the PageFlipHandler // this is called from the PageFlipHandler
trace!(self.logger, "Releasing old front buffer"); trace!(self.logger, "Releasing old front buffer");
self.front_buffer.set(self.next_buffer.replace(None)); let mut buffers = self.buffers.lock().unwrap();
buffers.front_buffer = buffers.next_buffer.take();
// drop and release the old buffer // drop and release the old buffer
} }
pub unsafe fn page_flip(&self) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> { pub unsafe fn page_flip(&self) -> Result<(), Error<<S as Surface>::Error>> {
let res = { let (result, fb) = {
let nb = self.next_buffer.take(); let mut buffers = self.buffers.lock().unwrap();
let res = nb.is_some(); if buffers.next_buffer.is_some() {
self.next_buffer.set(nb); // We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
res warn!(self.logger, "Tried to swap with an already queued flip");
}; return Err(Error::FrontBuffersExhausted);
if res {
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
warn!(self.logger, "Tried to swap with an already queued flip");
return Err(Error::FrontBuffersExhausted);
}
// supporting only one buffer would cause a lot of inconvinience and
// would most likely result in a lot of flickering.
// 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
.borrow()
.lock_front_buffer()
.map_err(|_| Error::FrontBufferLockFailed)?;
// create a framebuffer if the front buffer does not have one already
// (they are reused internally by gbm)
let maybe_fb = next_bo
.userdata()
.map_err(|_| Error::InvalidInternalState)?
.cloned();
let fb = if let Some(info) = maybe_fb {
info
} else {
let fb = self
.crtc
.add_planar_framebuffer(&*next_bo, &[0; 4], 0)
.compat()
.map_err(Error::FramebufferCreationFailed)?;
next_bo.set_userdata(fb).unwrap();
fb
};
self.next_buffer.set(Some(next_bo));
if cfg!(debug_assertions) {
if let Err(err) = self.crtc.get_framebuffer(fb) {
error!(self.logger, "Cached framebuffer invalid: {:?}: {}", fb, err);
} }
}
// if we re-created the surface, we need to commit the new changes, as we might trigger a modeset // supporting only one buffer would cause a lot of inconvinience and
let result = if self.recreated.get() { // would most likely result in a lot of flickering.
debug!(self.logger, "Commiting new state"); // neither weston, wlc or wlroots bother with that as well.
self.crtc.commit(fb).map_err(Error::Underlying) // so we just assume we got at least two buffers to do flipping.
} else { let mut next_bo = self
trace!(self.logger, "Queueing Page flip"); .surface
RawSurface::page_flip(&self.crtc, fb).map_err(Error::Underlying) .lock()
.unwrap()
.lock_front_buffer()
.map_err(|_| Error::FrontBufferLockFailed)?;
// create a framebuffer if the front buffer does not have one already
// (they are reused by gbm)
let maybe_fb = next_bo
.userdata()
.map_err(|_| Error::InvalidInternalState)?
.cloned();
let fb = if let Some(info) = maybe_fb {
info
} else {
let fb = self
.crtc
.add_planar_framebuffer(&next_bo, &[0; 4], 0)
.compat()
.map_err(Error::FramebufferCreationFailed)?;
next_bo.set_userdata(fb).unwrap();
fb
};
buffers.next_buffer = Some(next_bo);
if cfg!(debug_assertions) {
if let Err(err) = self.crtc.get_framebuffer(fb) {
error!(self.logger, "Cached framebuffer invalid: {:?}: {}", fb, err);
}
}
// if we re-created the surface, we need to commit the new changes, as we might trigger a modeset
(
if self.recreated.load(Ordering::SeqCst) {
debug!(self.logger, "Commiting new state");
self.crtc.commit(fb).map_err(Error::Underlying)
} else {
trace!(self.logger, "Queueing Page flip");
RawSurface::page_flip(&self.crtc, fb).map_err(Error::Underlying)
},
fb,
)
}; };
// if it was successful, we can clear the re-created state // if it was successful, we can clear the re-created state
match result { match result {
Ok(_) => { Ok(_) => {
self.recreated.set(false); self.recreated.store(false, Ordering::SeqCst);
self.current_frame_buffer.set(Some(fb)); let mut buffers = self.buffers.lock().unwrap();
buffers.current_frame_buffer = Some(fb);
Ok(()) Ok(())
} }
Err(err) => { Err(err) => {
@ -106,7 +162,7 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
} }
// this function is called, if we e.g. need to create the surface to match a new mode. // this function is called, if we e.g. need to create the surface to match a new mode.
pub fn recreate(&self) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> { pub fn recreate(&self) -> Result<(), Error<<S as Surface>::Error>> {
let (w, h) = self.pending_mode().size(); let (w, h) = self.pending_mode().size();
// Recreate the surface and the related resources to match the new // Recreate the surface and the related resources to match the new
@ -114,7 +170,8 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
debug!(self.logger, "(Re-)Initializing surface (with mode: {}:{})", w, h); debug!(self.logger, "(Re-)Initializing surface (with mode: {}:{})", w, h);
let surface = self let surface = self
.dev .dev
.borrow_mut() .lock()
.unwrap()
.create_surface( .create_surface(
w as u32, w as u32,
h as u32, h as u32,
@ -127,9 +184,9 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
self.clear_framebuffers(); self.clear_framebuffers();
// Drop the old surface after cleanup // Drop the old surface after cleanup
*self.surface.borrow_mut() = surface; *self.surface.lock().unwrap() = surface;
self.recreated.set(true); self.recreated.store(true, Ordering::SeqCst);
Ok(()) Ok(())
} }
@ -137,7 +194,8 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
// if the underlying drm-device is closed and re-opened framebuffers may get invalided. // if the underlying drm-device is closed and re-opened framebuffers may get invalided.
// here we clear them just to be sure, they get recreated on the next page_flip. // here we clear them just to be sure, they get recreated on the next page_flip.
pub fn clear_framebuffers(&self) { pub fn clear_framebuffers(&self) {
if let Some(Ok(Some(fb))) = self.next_buffer.take().map(|mut bo| bo.take_userdata()) { let mut buffers = self.buffers.lock().unwrap();
if let Some(Ok(Some(fb))) = buffers.next_buffer.take().map(|mut bo| bo.take_userdata()) {
if let Err(err) = self.crtc.destroy_framebuffer(fb) { if let Err(err) = self.crtc.destroy_framebuffer(fb) {
warn!( warn!(
self.logger, self.logger,
@ -146,7 +204,7 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
} }
} }
if let Some(Ok(Some(fb))) = self.front_buffer.take().map(|mut bo| bo.take_userdata()) { if let Some(Ok(Some(fb))) = buffers.front_buffer.take().map(|mut bo| bo.take_userdata()) {
if let Err(err) = self.crtc.destroy_framebuffer(fb) { if let Err(err) = self.crtc.destroy_framebuffer(fb) {
warn!( warn!(
self.logger, self.logger,
@ -157,9 +215,9 @@ impl<D: RawDevice + 'static> GbmSurfaceInternal<D> {
} }
} }
impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> { impl<S: RawSurface + 'static> Surface for GbmSurfaceInternal<S> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors; type Connectors = <S as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<S as Surface>::Error>;
fn crtc(&self) -> crtc::Handle { fn crtc(&self) -> crtc::Handle {
self.crtc.crtc() self.crtc.crtc()
@ -199,13 +257,13 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
} }
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
impl<D: RawDevice + 'static> CursorBackend for GbmSurfaceInternal<D> impl<S: RawSurface + 'static> CursorBackend for GbmSurfaceInternal<S>
where where
<D as RawDevice>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>, S: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
<<D as RawDevice>::Surface as CursorBackend>::Error: ::std::error::Error + Send, <S as CursorBackend>::Error: ::std::error::Error + Send,
{ {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<<<D as Device>::Surface as CursorBackend>::Error>; type Error = Error<<S as CursorBackend>::Error>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
self.crtc.set_cursor_position(x, y).map_err(Error::Underlying) self.crtc.set_cursor_position(x, y).map_err(Error::Underlying)
@ -222,7 +280,8 @@ where
// import the cursor into a buffer we can render // import the cursor into a buffer we can render
let mut cursor = self let mut cursor = self
.dev .dev
.borrow_mut() .lock()
.unwrap()
.create_buffer_object( .create_buffer_object(
w, w,
h, h,
@ -243,12 +302,12 @@ where
.map_err(Error::Underlying)?; .map_err(Error::Underlying)?;
// and store it // and store it
self.cursor.set((cursor, hotspot)); *self.cursor.lock().unwrap() = (cursor, hotspot);
Ok(()) Ok(())
} }
} }
impl<D: RawDevice + 'static> Drop for GbmSurfaceInternal<D> { impl<S: RawSurface + 'static> Drop for GbmSurfaceInternal<S> {
fn drop(&mut self) { fn drop(&mut self) {
// Drop framebuffers attached to the userdata of the gbm surface buffers. // Drop framebuffers attached to the userdata of the gbm surface buffers.
// (They don't implement drop, as they need the device) // (They don't implement drop, as they need the device)
@ -257,9 +316,9 @@ impl<D: RawDevice + 'static> Drop for GbmSurfaceInternal<D> {
} }
/// Gbm surface for rendering /// Gbm surface for rendering
pub struct GbmSurface<D: RawDevice + 'static>(pub(super) Rc<GbmSurfaceInternal<D>>); pub struct GbmSurface<S: RawSurface + 'static>(pub(super) Arc<GbmSurfaceInternal<S>>);
impl<D: RawDevice + 'static> GbmSurface<D> { impl<S: RawSurface + 'static> GbmSurface<S> {
/// Flips the underlying buffers. /// Flips the underlying buffers.
/// ///
/// The surface will report being already flipped until the matching event /// The surface will report being already flipped until the matching event
@ -295,9 +354,9 @@ impl<D: RawDevice + 'static> GbmSurface<D> {
} }
} }
impl<D: RawDevice + 'static> Surface for GbmSurface<D> { impl<S: RawSurface + 'static> Surface for GbmSurface<S> {
type Connectors = <<D as Device>::Surface as Surface>::Connectors; type Connectors = <S as Surface>::Connectors;
type Error = Error<<<D as Device>::Surface as Surface>::Error>; type Error = Error<<S as Surface>::Error>;
fn crtc(&self) -> crtc::Handle { fn crtc(&self) -> crtc::Handle {
self.0.crtc() self.0.crtc()
@ -337,13 +396,13 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
} }
#[cfg(feature = "backend_drm")] #[cfg(feature = "backend_drm")]
impl<D: RawDevice + 'static> CursorBackend for GbmSurface<D> impl<S: RawSurface + 'static> CursorBackend for GbmSurface<S>
where where
<D as RawDevice>::Surface: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>, S: CursorBackend<CursorFormat = dyn drm::buffer::Buffer>,
<<D as RawDevice>::Surface as CursorBackend>::Error: ::std::error::Error + Send, <S as CursorBackend>::Error: ::std::error::Error + Send,
{ {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
type Error = Error<<<D as Device>::Surface as CursorBackend>::Error>; type Error = Error<<S as CursorBackend>::Error>;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
self.0.set_cursor_position(x, y) self.0.set_cursor_position(x, y)
@ -357,3 +416,17 @@ where
self.0.set_cursor_representation(buffer, hotspot) self.0.set_cursor_representation(buffer, hotspot)
} }
} }
#[cfg(test)]
mod test {
use super::GbmSurface;
use crate::backend::drm::legacy::LegacyDrmSurface;
use std::fs::File;
fn is_send<S: Send>() {}
#[test]
fn surface_is_send() {
is_send::<GbmSurface<LegacyDrmSurface<File>>>();
}
}

View File

@ -29,9 +29,9 @@ use nix::sys::stat::fstat;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::{Arc, Weak};
use failure::{Fail, ResultExt}; use failure::{Fail, ResultExt};
@ -44,7 +44,7 @@ pub mod session;
/// Open raw drm device utilizing legacy mode-setting /// Open raw drm device utilizing legacy mode-setting
pub struct LegacyDrmDevice<A: AsRawFd + 'static> { pub struct LegacyDrmDevice<A: AsRawFd + 'static> {
dev: Rc<Dev<A>>, dev: Arc<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>, backends: Rc<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>,
@ -205,7 +205,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
} }
Ok(LegacyDrmDevice { Ok(LegacyDrmDevice {
dev: Rc::new(dev), dev: Arc::new(dev),
dev_id, dev_id,
active, active,
backends: Rc::new(RefCell::new(HashMap::new())), backends: Rc::new(RefCell::new(HashMap::new())),
@ -324,7 +324,7 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
return Err(Error::SurfaceWithoutConnectors(crtc)); return Err(Error::SurfaceWithoutConnectors(crtc));
} }
let backend = Rc::new(LegacyDrmSurfaceInternal::new( let backend = Arc::new(LegacyDrmSurfaceInternal::new(
self.dev.clone(), self.dev.clone(),
crtc, crtc,
mode, mode,
@ -332,7 +332,7 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
self.logger.new(o!("crtc" => format!("{:?}", crtc))), self.logger.new(o!("crtc" => format!("{:?}", crtc))),
)?); )?);
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend)); self.backends.borrow_mut().insert(crtc, Arc::downgrade(&backend));
Ok(LegacyDrmSurface(backend)) Ok(LegacyDrmSurface(backend))
} }

View File

@ -13,7 +13,7 @@ use std::collections::{HashMap, HashSet};
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::{Arc, Weak as WeakArc};
use super::{Dev, DevPath, Error, LegacyDrmDevice, LegacyDrmSurfaceInternal}; use super::{Dev, DevPath, Error, LegacyDrmDevice, LegacyDrmSurfaceInternal};
use crate::{ use crate::{
@ -25,18 +25,18 @@ use crate::{
/// linked to the [`LegacyDrmDevice`](LegacyDrmDevice) /// linked to the [`LegacyDrmDevice`](LegacyDrmDevice)
/// it was created from. /// it was created from.
pub(crate) struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> { pub(crate) struct LegacyDrmDeviceObserver<A: AsRawFd + 'static> {
dev: Weak<Dev<A>>, dev: WeakArc<Dev<A>>,
dev_id: dev_t, dev_id: dev_t,
privileged: bool, privileged: bool,
active: Arc<AtomicBool>, active: Arc<AtomicBool>,
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<LegacyDrmSurfaceInternal<A>>>>>, backends: Weak<RefCell<HashMap<crtc::Handle, WeakArc<LegacyDrmSurfaceInternal<A>>>>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<A: AsRawFd + 'static> Linkable<SessionSignal> for LegacyDrmDevice<A> { impl<A: AsRawFd + 'static> Linkable<SessionSignal> for LegacyDrmDevice<A> {
fn link(&mut self, signaler: Signaler<SessionSignal>) { fn link(&mut self, signaler: Signaler<SessionSignal>) {
let mut observer = LegacyDrmDeviceObserver { let mut observer = LegacyDrmDeviceObserver {
dev: Rc::downgrade(&self.dev), dev: Arc::downgrade(&self.dev),
dev_id: self.dev_id, dev_id: self.dev_id,
active: self.active.clone(), active: self.active.clone(),
privileged: self.dev.privileged, privileged: self.dev.privileged,
@ -69,7 +69,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDeviceObserver<A> {
} }
if let Some(device) = self.dev.upgrade() { if let Some(device) = self.dev.upgrade() {
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) { for surface in backends.borrow().values().filter_map(WeakArc::upgrade) {
// other ttys that use no cursor, might not clear it themselves. // other ttys that use no cursor, might not clear it themselves.
// This makes sure our cursor won't stay visible. // This makes sure our cursor won't stay visible.
// //
@ -139,7 +139,7 @@ impl<A: AsRawFd + 'static> LegacyDrmDeviceObserver<A> {
let mut used_connectors = HashSet::new(); let mut used_connectors = HashSet::new();
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) { for surface in backends.borrow().values().filter_map(WeakArc::upgrade) {
let mut current = surface.state.write().unwrap(); let mut current = surface.state.write().unwrap();
let pending = surface.pending.read().unwrap(); let pending = surface.pending.read().unwrap();

View File

@ -7,8 +7,7 @@ use drm::Device as BasicDevice;
use std::collections::HashSet; use std::collections::HashSet;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc; use std::sync::{atomic::Ordering, Arc, RwLock};
use std::sync::{atomic::Ordering, RwLock};
use crate::backend::drm::{common::Error, DevPath, RawSurface, Surface}; use crate::backend::drm::{common::Error, DevPath, RawSurface, Surface};
use crate::backend::graphics::CursorBackend; use crate::backend::graphics::CursorBackend;
@ -24,7 +23,7 @@ pub struct State {
} }
pub(in crate::backend::drm) struct LegacyDrmSurfaceInternal<A: AsRawFd + 'static> { pub(in crate::backend::drm) struct LegacyDrmSurfaceInternal<A: AsRawFd + 'static> {
pub(super) dev: Rc<Dev<A>>, pub(super) dev: Arc<Dev<A>>,
pub(in crate::backend::drm) crtc: crtc::Handle, pub(in crate::backend::drm) crtc: crtc::Handle,
pub(super) state: RwLock<State>, pub(super) state: RwLock<State>,
pub(super) pending: RwLock<State>, pub(super) pending: RwLock<State>,
@ -314,7 +313,7 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
impl<A: AsRawFd + 'static> LegacyDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> LegacyDrmSurfaceInternal<A> {
pub(crate) fn new( pub(crate) fn new(
dev: Rc<Dev<A>>, dev: Arc<Dev<A>>,
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: &[connector::Handle], connectors: &[connector::Handle],
@ -472,7 +471,7 @@ impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> {
/// Open raw crtc utilizing legacy mode-setting /// Open raw crtc utilizing legacy mode-setting
pub struct LegacyDrmSurface<A: AsRawFd + 'static>( pub struct LegacyDrmSurface<A: AsRawFd + 'static>(
pub(in crate::backend::drm) Rc<LegacyDrmSurfaceInternal<A>>, pub(in crate::backend::drm) Arc<LegacyDrmSurfaceInternal<A>>,
); );
impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurface<A> { impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurface<A> {
@ -555,3 +554,16 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurface<A> {
RawSurface::page_flip(&*self.0, framebuffer) RawSurface::page_flip(&*self.0, framebuffer)
} }
} }
#[cfg(test)]
mod test {
use super::LegacyDrmSurface;
use std::fs::File;
fn is_send<S: Send>() {}
#[test]
fn surface_is_send() {
is_send::<LegacyDrmSurface<File>>();
}
}

View File

@ -7,7 +7,7 @@ use crate::backend::egl::{native, EGLSurface};
use crate::backend::graphics::PixelFormat; use crate::backend::graphics::PixelFormat;
use std::os::raw::c_int; use std::os::raw::c_int;
use std::ptr; use std::ptr;
use std::sync::Arc; use std::sync::{atomic::Ordering, Arc};
/// EGL context for rendering /// EGL context for rendering
pub struct EGLContext { pub struct EGLContext {
@ -16,6 +16,9 @@ pub struct EGLContext {
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat, pixel_format: PixelFormat,
} }
// EGLContexts can be moved between threads safely
unsafe impl Send for EGLContext {}
unsafe impl Sync for EGLContext {}
impl EGLContext { impl EGLContext {
/// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay) /// Create a new [`EGLContext`] from a given [`NativeDisplay`](native::NativeDisplay)
@ -124,8 +127,7 @@ impl EGLContext {
where where
N: NativeSurface, N: NativeSurface,
{ {
let surface_ptr = surface.surface.get(); let surface_ptr = surface.surface.load(Ordering::SeqCst);
wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context)) wrap_egl_call(|| ffi::egl::MakeCurrent(**self.display, surface_ptr, surface_ptr, self.context))
.map(|_| ()) .map(|_| ())
.map_err(Into::into) .map_err(Into::into)

View File

@ -32,6 +32,9 @@ pub struct EGLDisplayHandle {
/// ffi EGLDisplay ptr /// ffi EGLDisplay ptr
pub handle: ffi::egl::types::EGLDisplay, pub handle: ffi::egl::types::EGLDisplay,
} }
// EGLDisplay has an internal Mutex
unsafe impl Send for EGLDisplayHandle {}
unsafe impl Sync for EGLDisplayHandle {}
impl Deref for EGLDisplayHandle { impl Deref for EGLDisplayHandle {
type Target = ffi::egl::types::EGLDisplay; type Target = ffi::egl::types::EGLDisplay;

View File

@ -4,22 +4,26 @@ use super::{ffi, native, EGLError, SurfaceCreationError, SwapBuffersError};
use crate::backend::egl::display::EGLDisplayHandle; use crate::backend::egl::display::EGLDisplayHandle;
use crate::backend::graphics::PixelFormat; use crate::backend::graphics::PixelFormat;
use nix::libc::c_int; use nix::libc::c_int;
use std::sync::Arc; use std::ops::{Deref, DerefMut};
use std::{ use std::sync::{
cell::Cell, atomic::{AtomicPtr, Ordering},
ops::{Deref, DerefMut}, Arc,
}; };
/// EGL surface of a given EGL context for rendering /// EGL surface of a given EGL context for rendering
pub struct EGLSurface<N: native::NativeSurface> { pub struct EGLSurface<N: native::NativeSurface> {
pub(crate) display: Arc<EGLDisplayHandle>, pub(crate) display: Arc<EGLDisplayHandle>,
native: N, native: N,
pub(crate) surface: Cell<ffi::egl::types::EGLSurface>, pub(crate) surface: AtomicPtr<nix::libc::c_void>,
config_id: ffi::egl::types::EGLConfig, config_id: ffi::egl::types::EGLConfig,
pixel_format: PixelFormat, pixel_format: PixelFormat,
surface_attributes: Vec<c_int>, surface_attributes: Vec<c_int>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
// safe because EGLConfig can be moved between threads
// and the other types are thread-safe
unsafe impl<N: native::NativeSurface + Send> Send for EGLSurface<N> {}
unsafe impl<N: native::NativeSurface + Send + Sync> Sync for EGLSurface<N> {}
impl<N: native::NativeSurface> Deref for EGLSurface<N> { impl<N: native::NativeSurface> Deref for EGLSurface<N> {
type Target = N; type Target = N;
@ -80,7 +84,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
Ok(EGLSurface { Ok(EGLSurface {
display, display,
native, native,
surface: Cell::new(surface), surface: AtomicPtr::new(surface as *mut _),
config_id: config, config_id: config,
pixel_format, pixel_format,
surface_attributes, surface_attributes,
@ -90,7 +94,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
/// Swaps buffers at the end of a frame. /// Swaps buffers at the end of a frame.
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> { pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError<N::Error>> {
let surface = self.surface.get(); let surface = self.surface.load(Ordering::SeqCst);
let result = if !surface.is_null() { let result = if !surface.is_null() {
self.native.swap_buffers(&self.display, surface) self.native.swap_buffers(&self.display, surface)
@ -106,21 +110,25 @@ impl<N: native::NativeSurface> EGLSurface<N> {
}; };
if self.native.needs_recreation() || surface.is_null() || is_bad_surface { if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
if !surface.is_null() { let previous = self.surface.compare_and_swap(
surface,
unsafe {
self.native
.create(&self.display, self.config_id, &self.surface_attributes)
.map_err(|err| match err {
SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
SwapBuffersError::EGLCreateWindowSurface(err)
}
SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
SwapBuffersError::Underlying(err)
}
})? as *mut _
},
Ordering::SeqCst,
);
if previous == surface && !surface.is_null() {
let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) }; let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
} }
self.surface.set(unsafe {
self.native
.create(&self.display, self.config_id, &self.surface_attributes)
.map_err(|err| match err {
SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
SwapBuffersError::EGLCreateWindowSurface(err)
}
SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
SwapBuffersError::Underlying(err)
}
})?
});
// if a recreation is pending anyway, ignore page-flip errors. // if a recreation is pending anyway, ignore page-flip errors.
// lets see if we still fail after the next commit. // lets see if we still fail after the next commit.
@ -135,9 +143,10 @@ impl<N: native::NativeSurface> EGLSurface<N> {
/// Returns true if the OpenGL surface is the current one in the thread. /// Returns true if the OpenGL surface is the current one in the thread.
pub fn is_current(&self) -> bool { pub fn is_current(&self) -> bool {
let surface = self.surface.load(Ordering::SeqCst);
unsafe { unsafe {
ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface.get() as *const _ ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == surface as *const _
&& ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface.get() as *const _ && ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == surface as *const _
} }
} }
@ -155,7 +164,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
impl<N: native::NativeSurface> Drop for EGLSurface<N> { impl<N: native::NativeSurface> Drop for EGLSurface<N> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {
ffi::egl::DestroySurface(**self.display, self.surface.get() as *const _); ffi::egl::DestroySurface(**self.display, *self.surface.get_mut() as *const _);
} }
} }
} }