Store DrmBackend in EventLoop state

This commit is contained in:
Drakulix 2017-09-20 19:48:58 +02:00
parent 0758ec98ba
commit 518f7dbdfc
7 changed files with 228 additions and 303 deletions

View File

@ -40,7 +40,4 @@ backend_libinput = ["input"]
renderer_glium = ["glium"] renderer_glium = ["glium"]
[replace] [replace]
"wayland-server:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"wayland-protocols:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"wayland-client:0.9.9" = { git = "https://github.com/Drakulix/wayland-rs", branch = "raw_handler_access"}
"drm:0.2.1" = { git = "https://github.com/Drakulix/drm-rs", branch = "future" } "drm:0.2.1" = { git = "https://github.com/Drakulix/drm-rs", branch = "future" }

View File

@ -19,9 +19,8 @@ use drm::control::encoder::Info as EncoderInfo;
use glium::Surface; use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData}; use helpers::{shell_implementation, surface_implementation, GliumDrawer, Roles, SurfaceData};
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler, Id}; use smithay::backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::{GliumGraphicsBackend, IntoGlium};
use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction}; use smithay::compositor::{compositor_init, CompositorToken, SubsurfaceRole, TraversalAction};
use smithay::compositor::roles::Role; use smithay::compositor::roles::Role;
use smithay::shell::{shell_init, ShellState}; use smithay::shell::{shell_init, ShellState};
@ -48,7 +47,7 @@ fn main() {
let mut options = OpenOptions::new(); let mut options = OpenOptions::new();
options.read(true); options.read(true);
options.write(true); options.write(true);
let mut device = let mut device: DrmDevice<GliumDrawer<DrmBackend>> =
DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap(); DrmDevice::new_from_file(options.clone().open("/dev/dri/card0").unwrap(), log.clone()).unwrap();
// Get a set of all modesetting resource handles (excluding planes): // Get a set of all modesetting resource handles (excluding planes):
@ -80,8 +79,8 @@ fn main() {
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
// Initialize the hardware backend // Initialize the hardware backend
let renderer = device let renderer_token = device
.create_backend(crtc, mode, vec![connector_info.handle()]) .create_backend(&mut event_loop, crtc, mode, vec![connector_info.handle()])
.unwrap(); .unwrap();
/* /*
@ -104,10 +103,12 @@ fn main() {
/* /*
* Initialize glium * Initialize glium
*/ */
let drawer = GliumDrawer::new(renderer.into_glium()); {
let mut frame = drawer.draw(); let drawer = event_loop.state().get(&renderer_token);
frame.clear_color(0.8, 0.8, 0.9, 1.0); let mut frame = drawer.draw();
frame.finish().unwrap(); frame.clear_color(0.8, 0.8, 0.9, 1.0);
frame.finish().unwrap();
}
/* /*
* Add a listening socket: * Add a listening socket:
@ -122,7 +123,6 @@ fn main() {
&mut event_loop, &mut event_loop,
device, device,
DrmHandlerImpl { DrmHandlerImpl {
drawer,
shell_state_token, shell_state_token,
compositor_token, compositor_token,
logger: log, logger: log,
@ -133,20 +133,21 @@ fn main() {
} }
pub struct DrmHandlerImpl { pub struct DrmHandlerImpl {
drawer: GliumDrawer<GliumGraphicsBackend<DrmBackend>>,
shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>, shell_state_token: StateToken<ShellState<SurfaceData, Roles, (), ()>>,
compositor_token: CompositorToken<SurfaceData, Roles, ()>, compositor_token: CompositorToken<SurfaceData, Roles, ()>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl DrmHandler for DrmHandlerImpl { impl DrmHandler<GliumDrawer<DrmBackend>> for DrmHandlerImpl {
fn ready(&mut self, evlh: &mut EventLoopHandle, _id: Id, _frame: u32, _duration: Duration) { fn ready(&mut self, evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
let mut frame = self.drawer.draw(); backend: &StateToken<GliumDrawer<DrmBackend>>, _frame: u32, _duration: Duration) {
let state = evlh.state();
let drawer = state.get(backend);
let mut frame = drawer.draw();
frame.clear_color(0.8, 0.8, 0.9, 1.0); frame.clear_color(0.8, 0.8, 0.9, 1.0);
// redraw the frame, in a simple but inneficient way // redraw the frame, in a simple but inneficient way
{ {
let screen_dimensions = self.drawer.get_framebuffer_dimensions(); let screen_dimensions = drawer.get_framebuffer_dimensions();
let state = evlh.state();
for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() { for toplevel_surface in state.get(&self.shell_state_token).toplevel_surfaces() {
if let Some(wl_surface) = toplevel_surface.get_surface() { if let Some(wl_surface) = toplevel_surface.get_surface() {
// this surface is a root of a subsurface tree that needs to be drawn // this surface is a root of a subsurface tree that needs to be drawn
@ -163,13 +164,7 @@ impl DrmHandler for DrmHandlerImpl {
x += subdata.x; x += subdata.x;
y += subdata.y; y += subdata.y;
} }
self.drawer.render( drawer.render(&mut frame, contents, (w, h), (x, y), screen_dimensions);
&mut frame,
contents,
(w, h),
(x, y),
screen_dimensions,
);
TraversalAction::DoChildren((x, y)) TraversalAction::DoChildren((x, y))
} else { } else {
// we are not display, so our children are neither // we are not display, so our children are neither
@ -184,7 +179,8 @@ impl DrmHandler for DrmHandlerImpl {
frame.finish().unwrap(); frame.finish().unwrap();
} }
fn error(&mut self, _evlh: &mut EventLoopHandle, error: IoError) { fn error(&mut self, _evlh: &mut EventLoopHandle, _device: &mut DrmDevice<GliumDrawer<DrmBackend>>,
error: IoError) {
panic!("{:?}", error); panic!("{:?}", error);
} }
} }

View File

@ -1,7 +1,8 @@
use glium; use glium;
use glium::Surface; use glium::{Frame, Surface};
use glium::index::PrimitiveType; use glium::index::PrimitiveType;
use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::GliumGraphicsBackend;
use std::ops::Deref; use std::ops::Deref;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -12,23 +13,25 @@ struct Vertex {
implement_vertex!(Vertex, position, tex_coords); implement_vertex!(Vertex, position, tex_coords);
pub struct GliumDrawer<F> { pub struct GliumDrawer<F: EGLGraphicsBackend + 'static> {
display: F, display: GliumGraphicsBackend<F>,
vertex_buffer: glium::VertexBuffer<Vertex>, vertex_buffer: glium::VertexBuffer<Vertex>,
index_buffer: glium::IndexBuffer<u16>, index_buffer: glium::IndexBuffer<u16>,
program: glium::Program, program: glium::Program,
} }
impl<F> Deref for GliumDrawer<F> { impl<F: EGLGraphicsBackend + 'static> Deref for GliumDrawer<F> {
type Target = F; type Target = F;
fn deref(&self) -> &F { fn deref(&self) -> &F {
&self.display &*self.display
} }
} }
impl<F: glium::backend::Facade> GliumDrawer<F> { impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> for GliumDrawer<T> {
pub fn new(display: F) -> GliumDrawer<F> { fn from(backend: T) -> GliumDrawer<T> {
let display = backend.into();
// building the vertex buffer, which contains all the vertices that we will draw // building the vertex buffer, which contains all the vertices that we will draw
let vertex_buffer = glium::VertexBuffer::new( let vertex_buffer = glium::VertexBuffer::new(
&display, &display,
@ -93,7 +96,9 @@ impl<F: glium::backend::Facade> GliumDrawer<F> {
program, program,
} }
} }
}
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32),
surface_location: (i32, i32), screen_size: (u32, u32)) { surface_location: (i32, i32), screen_size: (u32, u32)) {
let image = glium::texture::RawImage2d { let image = glium::texture::RawImage2d {
@ -130,4 +135,9 @@ impl<F: glium::backend::Facade> GliumDrawer<F> {
) )
.unwrap(); .unwrap();
} }
#[inline]
pub fn draw(&self) -> Frame {
self.display.draw()
}
} }

View File

@ -7,7 +7,6 @@ extern crate slog_async;
extern crate slog_term; extern crate slog_term;
#[macro_use(define_roles)] #[macro_use(define_roles)]
extern crate smithay; extern crate smithay;
extern crate wayland_protocols;
extern crate wayland_server; extern crate wayland_server;
mod helpers; mod helpers;
@ -16,8 +15,6 @@ use glium::Surface;
use helpers::{shell_implementation, surface_implementation, GliumDrawer}; use helpers::{shell_implementation, surface_implementation, GliumDrawer};
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend;
use smithay::backend::graphics::glium::IntoGlium;
use smithay::backend::input::InputBackend; use smithay::backend::input::InputBackend;
use smithay::backend::winit; use smithay::backend::winit;
use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction}; use smithay::compositor::{compositor_init, SubsurfaceRole, TraversalAction};
@ -57,7 +54,7 @@ fn main() {
/* /*
* Initialize glium * Initialize glium
*/ */
let drawer = GliumDrawer::new(renderer.into_glium()); let drawer = GliumDrawer::from(renderer);
/* /*
* Add a listening socket: * Add a listening socket:

View File

@ -8,18 +8,9 @@ use drm::control::ResourceInfo;
use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle}; use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
use nix::c_void; use nix::c_void;
use std::cell::{Cell, RefCell}; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
/// Backend based on a `DrmDevice` and a given crtc
pub struct DrmBackend(Rc<RefCell<DrmBackendInternal>>);
impl DrmBackend {
pub(crate) fn new(drm: Rc<RefCell<DrmBackendInternal>>) -> DrmBackend {
DrmBackend(drm)
}
}
/* /*
Dependency graph Dependency graph
- drm - drm
@ -32,7 +23,7 @@ impl DrmBackend {
*/ */
pub(crate) struct GbmTypes<'dev, 'context> { pub(crate) struct GbmTypes<'dev, 'context> {
cursor: BufferObject<'dev, ()>, cursor: Cell<BufferObject<'dev, ()>>,
surface: Surface<'context>, surface: Surface<'context>,
} }
@ -70,60 +61,32 @@ rental! {
} }
use self::graphics::{Graphics, Surface}; use self::graphics::{Graphics, Surface};
/// Id of a `DrmBackend` related to its `DrmDevice`.
///
/// Used to track which `DrmBackend` finished page-flipping
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Id(usize);
impl Id { /// Backend based on a `DrmDevice` and a given crtc
pub(crate) fn raw(&self) -> usize { pub struct DrmBackend {
self.0
}
}
pub(crate) struct DrmBackendInternal {
graphics: Graphics, graphics: Graphics,
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: Vec<connector::Handle>, connectors: Vec<connector::Handle>,
own_id: Id,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl DrmBackendInternal { impl DrmBackend {
pub(crate) fn new<I, L>(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: I, pub(crate) fn new(context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode,
own_id: usize, logger: L) connectors: Vec<connector::Handle>, logger: ::slog::Logger)
-> Result<DrmBackendInternal> -> Result<DrmBackend> {
where
I: Into<Vec<connector::Handle>>,
L: Into<Option<::slog::Logger>>,
{
// logger already initialized by the DrmDevice // logger already initialized by the DrmDevice
let log = ::slog_or_stdlog(logger); let log = ::slog_or_stdlog(logger);
info!(log, "Initializing DrmBackend"); info!(log, "Initializing DrmBackend");
let connectors = connectors.into();
// check the connectors, if they suite the mode
for connector in connectors.iter() {
if !connector::Info::load_from_device(context.head().head(), *connector)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.head().head())))?
.modes()
.contains(&mode)
{
bail!(ErrorKind::ModeNotSuitable(mode))
}
}
let (w, h) = mode.size(); let (w, h) = mode.size();
Ok(DrmBackendInternal { Ok(DrmBackend {
graphics: Graphics::try_new(context, |context| { graphics: Graphics::try_new(context, |context| {
Ok(GbmTypes { Ok(GbmTypes {
cursor: { cursor: {
// Create an unused cursor buffer (we don't want an Option here) // Create an unused cursor buffer (we don't want an Option here)
context Cell::new(context
.devices .devices
.gbm .gbm
.create_buffer_object( .create_buffer_object(
@ -132,7 +95,7 @@ impl DrmBackendInternal {
GbmFormat::ARGB8888, GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write], &[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
) )
.chain_err(|| ErrorKind::GbmInitFailed)? .chain_err(|| ErrorKind::GbmInitFailed)?)
}, },
surface: Surface::try_new( surface: Surface::try_new(
{ {
@ -202,7 +165,6 @@ impl DrmBackendInternal {
crtc, crtc,
mode, mode,
connectors, connectors,
own_id: Id(own_id),
logger: log.clone(), logger: log.clone(),
}) })
} }
@ -225,85 +187,67 @@ impl DrmBackendInternal {
}); });
} }
pub(crate) fn is_crtc(&self, crtc: crtc::Handle) -> bool {
crtc == self.crtc
}
}
impl DrmBackend {
/// Add a connector to backend /// Add a connector to backend
/// ///
/// # Errors /// # Errors
/// ///
/// Errors if the new connector does not support the currently set `Mode` /// Errors if the new connector does not support the currently set `Mode`
pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> { pub fn add_connector(&mut self, connector: connector::Handle) -> Result<()> {
let info = let info = connector::Info::load_from_device(self.graphics.head().head().head(), connector)
connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), connector) .chain_err(|| {
.chain_err(|| { ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
ErrorKind::DrmDev(format!( })?;
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})?;
// check if the connector can handle the current mode // check if the connector can handle the current mode
let mut internal = self.0.borrow_mut(); if info.modes().contains(&self.mode) {
if info.modes().contains(&internal.mode) {
// check if there is a valid encoder // check if there is a valid encoder
let encoders = info.encoders() let encoders = info.encoders()
.iter() .iter()
.map(|encoder| { .map(|encoder| {
encoder::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *encoder) encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder).chain_err(
.chain_err(|| { || ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())),
ErrorKind::DrmDev(format!( )
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})
}) })
.collect::<Result<Vec<encoder::Info>>>()?; .collect::<Result<Vec<encoder::Info>>>()?;
// and if any encoder supports the selected crtc // and if any encoder supports the selected crtc
if !encoders if !encoders
.iter() .iter()
.any(|encoder| encoder.supports_crtc(self.0.borrow().crtc)) .any(|encoder| encoder.supports_crtc(self.crtc))
{ {
bail!(ErrorKind::NoSuitableEncoder(info, self.0.borrow().crtc)); bail!(ErrorKind::NoSuitableEncoder(info, self.crtc));
} }
info!( info!(
self.0.borrow().logger, self.logger,
"Adding new connector: {:?}", "Adding new connector: {:?}",
info.connector_type() info.connector_type()
); );
internal.connectors.push(connector); self.connectors.push(connector);
Ok(()) Ok(())
} else { } else {
bail!(ErrorKind::ModeNotSuitable(self.0.borrow().mode)) bail!(ErrorKind::ModeNotSuitable(self.mode))
} }
} }
/// Returns a copy of the currently set connectors /// Returns the currently set connectors
pub fn used_connectors(&self) -> Vec<connector::Handle> { pub fn used_connectors(&self) -> &[connector::Handle] {
// thanks to the RefCell we can sadly not return a `&[connector::Handle]` &*self.connectors
self.0.borrow().connectors.clone()
} }
/// Removes a currently set connector /// Removes a currently set connector
pub fn remove_connector(&mut self, connector: connector::Handle) { pub fn remove_connector(&mut self, connector: connector::Handle) {
if let Ok(info) = if let Ok(info) = connector::Info::load_from_device(self.graphics.head().head().head(), connector) {
connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), connector)
{
info!( info!(
self.0.borrow().logger, self.logger,
"Removing connector: {:?}", "Removing connector: {:?}",
info.connector_type() info.connector_type()
); );
} else { } else {
info!(self.0.borrow().logger, "Removing unknown connector"); info!(self.logger, "Removing unknown connector");
} }
self.0.borrow_mut().connectors.retain(|x| *x != connector); self.connectors.retain(|x| *x != connector);
} }
/// Changes the currently set mode /// Changes the currently set mode
@ -315,13 +259,10 @@ impl DrmBackend {
/// Other errors might occur. /// Other errors might occur.
pub fn use_mode(&mut self, mode: Mode) -> Result<()> { pub fn use_mode(&mut self, mode: Mode) -> Result<()> {
// check the connectors // check the connectors
for connector in self.0.borrow().connectors.iter() { for connector in self.connectors.iter() {
if !connector::Info::load_from_device(self.0.borrow().graphics.head().head().head(), *connector) if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector)
.chain_err(|| { .chain_err(|| {
ErrorKind::DrmDev(format!( ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
"{:?}",
self.0.borrow().graphics.head().head().head()
))
})? })?
.modes() .modes()
.contains(&mode) .contains(&mode)
@ -332,21 +273,25 @@ impl DrmBackend {
// borrow & clone stuff because rust cannot figure out the upcoming // borrow & clone stuff because rust cannot figure out the upcoming
// closure otherwise. // closure otherwise.
let crtc = self.0.borrow().crtc; let crtc = self.crtc;
let mut internal = self.0.borrow_mut(); let connectors_ref = &self.connectors;
let connectors = internal.connectors.clone(); let logger_ref = &self.logger;
let logger = internal.logger.clone();
let (w, h) = mode.size(); let (w, h) = mode.size();
internal.graphics.rent_all_mut(|graphics| -> Result<()> { self.graphics.rent_all_mut(|graphics| -> Result<()> {
// Recreate the surface and the related resources to match the new // Recreate the surface and the related resources to match the new
// resolution. // resolution.
debug!(logger, "Reinitializing surface for new mode: {}:{}", w, h); debug!(
logger_ref,
"Reinitializing surface for new mode: {}:{}",
w,
h
);
graphics.gbm.surface = Surface::try_new( graphics.gbm.surface = Surface::try_new(
{ {
// create a new gbm surface // create a new gbm surface
debug!(logger, "Creating GbmSurface"); debug!(logger_ref, "Creating GbmSurface");
Box::new(graphics Box::new(graphics
.context .context
.devices .devices
@ -361,7 +306,7 @@ impl DrmBackend {
}, },
|surface| { |surface| {
// create an egl surface from the gbm one // create an egl surface from the gbm one
debug!(logger, "Creating EGLSurface"); debug!(logger_ref, "Creating EGLSurface");
let egl_surface = graphics.context.egl.create_surface(&surface)?; let egl_surface = graphics.context.egl.create_surface(&surface)?;
// make it active for the first `crtc::set` // make it active for the first `crtc::set`
@ -378,18 +323,22 @@ impl DrmBackend {
let mut front_bo = surface let mut front_bo = surface
.lock_front_buffer() .lock_front_buffer()
.chain_err(|| ErrorKind::FailedToSwap)?; .chain_err(|| ErrorKind::FailedToSwap)?;
debug!(logger, "FrontBuffer color format: {:?}", front_bo.format()); debug!(
logger_ref,
"FrontBuffer color format: {:?}",
front_bo.format()
);
// we need a framebuffer per front_buffer // we need a framebuffer per front_buffer
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| { let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo).chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)) ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm))
})?; })?;
debug!(logger, "Initialize screen"); debug!(logger_ref, "Initialize screen");
crtc::set( crtc::set(
graphics.context.devices.drm, graphics.context.devices.drm,
crtc, crtc,
fb.handle(), fb.handle(),
&connectors, connectors_ref,
(0, 0), (0, 0),
Some(mode), Some(mode),
).chain_err(|| { ).chain_err(|| {
@ -410,25 +359,17 @@ impl DrmBackend {
Ok(()) Ok(())
})?; })?;
info!(logger, "Setting new mode: {:?}", mode.name()); info!(self.logger, "Setting new mode: {:?}", mode.name());
internal.mode = mode; self.mode = mode;
Ok(()) Ok(())
} }
/// Checks of the `DrmBackend` is of the given `Id`
///
/// Only produces valid results, if the `Id` is from the `DrmDevice`,
/// that created this backend.
pub fn is(&self, id: Id) -> bool {
self.0.borrow().own_id == id
}
} }
impl Drop for DrmBackend { impl Drop for DrmBackend {
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)
self.0.borrow_mut().graphics.rent_all_mut(|graphics| { self.graphics.rent_all_mut(|graphics| {
if let Some(fb) = graphics.gbm.surface.rent(|egl| { if let Some(fb) = graphics.gbm.surface.rent(|egl| {
if let Some(mut next) = egl.buffers.next_buffer.take() { if let Some(mut next) = egl.buffers.next_buffer.take() {
return next.take_userdata(); return next.take_userdata();
@ -459,84 +400,72 @@ impl GraphicsBackend for DrmBackend {
type Error = Error; type Error = Error;
fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> { fn set_cursor_position(&self, x: u32, y: u32) -> Result<()> {
trace!(self.0.borrow().logger, "Move the cursor to {},{}", x, y); trace!(self.logger, "Move the cursor to {},{}", x, y);
crtc::move_cursor( crtc::move_cursor(
self.0.borrow().graphics.head().head().head(), self.graphics.head().head().head(),
self.0.borrow().crtc, self.crtc,
(x as i32, y as i32), (x as i32, y as i32),
).chain_err(|| { ).chain_err(|| {
ErrorKind::DrmDev(format!( ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
"{:?}",
self.0.borrow().graphics.head().head().head()
))
}) })
} }
fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)) fn set_cursor_representation(&self, buffer: ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32))
-> Result<()> { -> Result<()> {
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
debug!(self.0.borrow().logger, "Importing cursor");
// import the cursor into a buffer we can render
self.0
.borrow_mut()
.graphics
.rent_all_mut(|graphics| -> Result<()> {
graphics.gbm.cursor = {
let mut cursor = graphics
.context
.devices
.gbm
.create_buffer_object(
w,
h,
GbmFormat::ARGB8888,
&[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
)
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
.write(&*buffer.into_raw())
.chain_err(|| ErrorKind::GbmInitFailed)?;
cursor
};
Ok(())
})?;
trace!(self.0.borrow().logger, "Set the new imported cursor"); debug!(self.logger, "Importing cursor");
// and set it
if crtc::set_cursor2( self.graphics.rent_all(|graphics| -> Result<()> {
self.0.borrow().graphics.head().head().head(), graphics.gbm.cursor.set({
self.0.borrow().crtc, // import the cursor into a buffer we can render
self.0 let mut cursor = graphics
.borrow() .context
.graphics .devices
.rent(|gbm| Buffer::handle(&gbm.cursor)), .gbm
(w, h), .create_buffer_object(
(hotspot.0 as i32, hotspot.1 as i32), w,
).is_err() h,
{ GbmFormat::ARGB8888,
crtc::set_cursor( &[BufferObjectFlags::Cursor, BufferObjectFlags::Write],
self.0.borrow().graphics.head().head().head(), )
self.0.borrow().crtc, .chain_err(|| ErrorKind::GbmInitFailed)?;
self.0 cursor
.borrow() .write(&*buffer.into_raw())
.graphics .chain_err(|| ErrorKind::GbmInitFailed)?;
.rent(|gbm| Buffer::handle(&gbm.cursor)),
(w, h), trace!(self.logger, "Set the new imported cursor");
).chain_err(|| {
ErrorKind::DrmDev(format!( // and set it
"{:?}", if crtc::set_cursor2(
self.0.borrow().graphics.head().head().head() self.graphics.head().head().head(),
)) self.crtc,
}) Buffer::handle(&cursor),
} else { (w, h),
(hotspot.0 as i32, hotspot.1 as i32),
).is_err()
{
crtc::set_cursor(
self.graphics.head().head().head(),
self.crtc,
Buffer::handle(&cursor),
(w, h),
).chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))
})?;
}
// and store it
cursor
});
Ok(()) Ok(())
} })
} }
} }
impl EGLGraphicsBackend for DrmBackend { impl EGLGraphicsBackend for DrmBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0.borrow().graphics.rent_all(|graphics| { self.graphics.rent_all(|graphics| {
// We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done // We cannot call lock_front_buffer anymore without releasing the previous buffer, which will happen when the page flip is done
if graphics.gbm.surface.rent(|egl| { if graphics.gbm.surface.rent(|egl| {
let next = egl.buffers.next_buffer.take(); let next = egl.buffers.next_buffer.take();
@ -544,7 +473,7 @@ impl EGLGraphicsBackend for DrmBackend {
egl.buffers.next_buffer.set(next); egl.buffers.next_buffer.set(next);
res res
}) { }) {
warn!(self.0.borrow().logger, "Tried to swap a DrmBackend with a queued flip"); warn!(self.logger, "Tried to swap a DrmBackend with a queued flip");
return Err(SwapBuffersError::AlreadySwapped); return Err(SwapBuffersError::AlreadySwapped);
} }
@ -570,48 +499,36 @@ impl EGLGraphicsBackend for DrmBackend {
}; };
surface.egl.buffers.next_buffer.set(Some(next_bo)); surface.egl.buffers.next_buffer.set(Some(next_bo));
trace!(self.0.borrow().logger, "Queueing Page flip"); trace!(self.logger, "Queueing Page flip");
let id: Id = self.0.borrow().own_id;
// and flip // and flip
crtc::page_flip(graphics.context.devices.drm, self.0.borrow().crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent], id).map_err(|_| SwapBuffersError::ContextLost) crtc::page_flip(graphics.context.devices.drm, self.crtc, fb.handle(), &[crtc::PageFlipFlags::PageFlipEvent], self.crtc).map_err(|_| SwapBuffersError::ContextLost)
}) })
}) })
} }
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
self.0 self.graphics
.borrow()
.graphics
.head() .head()
.rent(|context| context.get_proc_address(symbol)) .rent(|context| context.get_proc_address(symbol))
} }
fn get_framebuffer_dimensions(&self) -> (u32, u32) { fn get_framebuffer_dimensions(&self) -> (u32, u32) {
let (w, h) = self.0.borrow().mode.size(); let (w, h) = self.mode.size();
(w as u32, h as u32) (w as u32, h as u32)
} }
fn is_current(&self) -> bool { fn is_current(&self) -> bool {
self.0 self.graphics.head().rent(|context| context.is_current())
.borrow()
.graphics
.head()
.rent(|context| context.is_current())
} }
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.0 self.graphics
.borrow()
.graphics
.rent(|gbm| gbm.surface.rent(|egl| egl.surface.make_current())) .rent(|gbm| gbm.surface.rent(|egl| egl.surface.make_current()))
} }
fn get_pixel_format(&self) -> PixelFormat { fn get_pixel_format(&self) -> PixelFormat {
self.0 self.graphics
.borrow()
.graphics
.head() .head()
.rent(|context| context.get_pixel_format()) .rent(|context| context.get_pixel_format())
} }

View File

@ -191,20 +191,21 @@ use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
use drm::control::Device as ControlDevice; use drm::control::Device as ControlDevice;
use gbm::Device as GbmDevice; use gbm::Device as GbmDevice;
use nix; use nix;
use std::cell::RefCell; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{Error as IoError, Result as IoResult}; use std::io::{Error as IoError, Result as IoResult};
use std::marker::PhantomData;
use std::ops::Deref;
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::time::Duration; use std::time::Duration;
use wayland_server::EventLoopHandle; use wayland_server::{EventLoopHandle, StateToken};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, READ};
mod backend; mod backend;
pub mod error; pub mod error;
pub use self::backend::{DrmBackend, Id}; pub use self::backend::DrmBackend;
use self::backend::DrmBackendInternal;
use self::error::*; use self::error::*;
/// Internal struct as required by the drm crate /// Internal struct as required by the drm crate
@ -255,13 +256,13 @@ rental! {
use self::devices::{Context, Devices}; use self::devices::{Context, Devices};
/// Representation of an open drm device node to create rendering backends /// Representation of an open drm device node to create rendering backends
pub struct DrmDevice { pub struct DrmDevice<B: Deref<Target = DrmBackend> + 'static> {
context: Rc<Context>, context: Rc<Context>,
backends: Vec<Weak<RefCell<DrmBackendInternal>>>, backends: HashMap<crtc::Handle, StateToken<B>>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl DrmDevice { impl<B: From<DrmBackend> + Deref<Target = DrmBackend> + 'static> DrmDevice<B> {
/// Create a new `DrmDevice` from a raw file descriptor /// Create a new `DrmDevice` from a raw file descriptor
/// ///
/// Returns an error of opening the device failed or context creation was not /// Returns an error of opening the device failed or context creation was not
@ -358,7 +359,7 @@ impl DrmDevice {
context: Rc::new(Context::try_new( context: Rc::new(Context::try_new(
Box::new(Devices::try_new(Box::new(drm), |drm| { Box::new(Devices::try_new(Box::new(drm), |drm| {
debug!(log, "Creating gbm device"); debug!(log, "Creating gbm device");
GbmDevice::new_from_drm::<DrmDevice>(drm).chain_err(|| ErrorKind::GbmInitFailed) GbmDevice::new_from_drm::<DrmDevice<B>>(drm).chain_err(|| ErrorKind::GbmInitFailed)
})?), })?),
|devices| { |devices| {
debug!(log, "Creating egl context from gbm device"); debug!(log, "Creating egl context from gbm device");
@ -375,7 +376,7 @@ impl DrmDevice {
).map_err(Error::from) ).map_err(Error::from)
}, },
)?), )?),
backends: Vec::new(), backends: HashMap::new(),
logger: log, logger: log,
}) })
} }
@ -385,29 +386,32 @@ impl DrmDevice {
/// ///
/// Errors if initialization fails or the mode is not available on all given /// Errors if initialization fails or the mode is not available on all given
/// connectors. /// connectors.
pub fn create_backend<I>(&mut self, crtc: crtc::Handle, mode: Mode, connectors: I) -> Result<DrmBackend> pub fn create_backend<I>(&mut self, evlh: &mut EventLoopHandle, crtc: crtc::Handle, mode: Mode,
connectors: I)
-> Result<StateToken<B>>
where where
I: Into<Vec<connector::Handle>>, I: Into<Vec<connector::Handle>>,
{ {
for backend in self.backends.iter() { if self.backends.contains_key(&crtc) {
if let Some(backend) = backend.upgrade() { bail!(ErrorKind::CrtcAlreadyInUse(crtc));
if backend.borrow().is_crtc(crtc) {
bail!(ErrorKind::CrtcAlreadyInUse(crtc))
}
}
} }
// check if the given connectors and crtc match // check if the given connectors and crtc match
let connectors = connectors.into(); let connectors = connectors.into();
// check if we have an encoder for every connector // check if we have an encoder for every connector and the mode mode
for connector in connectors.iter() { for connector in connectors.iter() {
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector) let con_info = connector::Info::load_from_device(self.context.head().head(), *connector)
.chain_err(|| { .chain_err(|| {
ErrorKind::DrmDev(format!("{:?}", self.context.head().head())) ErrorKind::DrmDev(format!("{:?}", self.context.head().head()))
})?; })?;
// then check for every connector which encoders it does support // check the mode
if !con_info.modes().contains(&mode) {
bail!(ErrorKind::ModeNotSuitable(mode));
}
// check for every connector which encoders it does support
let encoders = con_info let encoders = con_info
.encoders() .encoders()
.iter() .iter()
@ -426,59 +430,50 @@ impl DrmDevice {
// configuration is valid, the kernel will figure out the rest // configuration is valid, the kernel will figure out the rest
let own_id = self.backends.len(); let logger = self.logger.new(o!("crtc" => format!("{:?}", crtc)));
let logger = self.logger.new( let backend = DrmBackend::new(self.context.clone(), crtc, mode, connectors, logger)?;
o!("id" => format!("{}", own_id), "crtc" => format!("{:?}", crtc)), let token = evlh.state().insert(backend.into());
); self.backends.insert(crtc, token.clone());
let backend = Rc::new(RefCell::new(DrmBackendInternal::new( Ok(token)
self.context.clone(),
crtc,
mode,
connectors,
own_id,
logger,
)?));
self.backends.push(Rc::downgrade(&backend));
Ok(DrmBackend::new(backend))
} }
} }
// for users convinience and FdEventSource registering // for users convinience and FdEventSource registering
impl AsRawFd for DrmDevice { impl<B: Deref<Target = DrmBackend> + 'static> AsRawFd for DrmDevice<B> {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.context.head().head().as_raw_fd() self.context.head().head().as_raw_fd()
} }
} }
impl BasicDevice for DrmDevice {} impl<B: Deref<Target = DrmBackend> + 'static> BasicDevice for DrmDevice<B> {}
impl ControlDevice for DrmDevice {} impl<B: Deref<Target = DrmBackend> + 'static> ControlDevice for DrmDevice<B> {}
/// Handler for drm node events /// Handler for drm node events
/// ///
/// See module-level documentation for its use /// See module-level documentation for its use
pub trait DrmHandler { pub trait DrmHandler<B: Deref<Target = DrmBackend> + 'static> {
/// A `DrmBackend` has finished swapping buffers and new frame can now /// A `DrmBackend` has finished swapping buffers and new frame can now
/// (and should be immediately) be rendered. /// (and should be immediately) be rendered.
/// ///
/// The `id` argument is the `Id` of the `DrmBackend` that finished rendering, /// The `id` argument is the `Id` of the `DrmBackend` that finished rendering,
/// check using `DrmBackend::is`. /// check using `DrmBackend::is`.
fn ready(&mut self, evlh: &mut EventLoopHandle, id: Id, frame: u32, duration: Duration); fn ready(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, backend: &StateToken<B>,
frame: u32, duration: Duration);
/// The `DrmDevice` has thrown an error. /// The `DrmDevice` has thrown an error.
/// ///
/// The related backends are most likely *not* usable anymore and /// The related backends are most likely *not* usable anymore and
/// the whole stack has to be recreated. /// the whole stack has to be recreated.
fn error(&mut self, evlh: &mut EventLoopHandle, error: IoError); fn error(&mut self, evlh: &mut EventLoopHandle, device: &mut DrmDevice<B>, error: IoError);
} }
/// Bind a `DrmDevice` to an EventLoop, /// Bind a `DrmDevice` to an EventLoop,
/// ///
/// This will cause it to recieve events and feed them into an `DrmHandler` /// This will cause it to recieve events and feed them into an `DrmHandler`
pub fn drm_device_bind<H>(evlh: &mut EventLoopHandle, device: DrmDevice, handler: H) pub fn drm_device_bind<B, H>(evlh: &mut EventLoopHandle, device: DrmDevice<B>, handler: H)
-> IoResult<FdEventSource<(DrmDevice, H)>> -> IoResult<FdEventSource<(DrmDevice<B>, H)>>
where where
H: DrmHandler + 'static, B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{ {
evlh.add_fd_event_source( evlh.add_fd_event_source(
device.as_raw_fd(), device.as_raw_fd(),
@ -488,28 +483,44 @@ where
) )
} }
fn fd_event_source_implementation<H>() -> FdEventSourceImpl<(DrmDevice, H)> fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(DrmDevice<B>, H)>
where where
H: DrmHandler + 'static, B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{ {
FdEventSourceImpl { FdEventSourceImpl {
ready: |evlh, id, _, _| { ready: |evlh, id, _, _| {
use std::any::Any; use std::any::Any;
let &mut (ref dev, ref mut handler) = id; let &mut (ref mut dev, ref mut handler) = id;
struct PageFlipHandler<'a, 'b, H: DrmHandler + 'static>(&'a mut H, &'b mut EventLoopHandle); struct PageFlipHandler<
'a,
'b,
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
> {
handler: &'a mut H,
evlh: &'b mut EventLoopHandle,
_marker: PhantomData<B>,
};
impl<'a, 'b, H: DrmHandler + 'static> crtc::PageFlipHandler<DrmDevice> for PageFlipHandler<'a, 'b, H> { impl<'a, 'b, B, H> crtc::PageFlipHandler<DrmDevice<B>> for PageFlipHandler<'a, 'b, B, H>
fn handle_event(&mut self, device: &DrmDevice, frame: u32, duration: Duration, where
B: Deref<Target = DrmBackend> + 'static,
H: DrmHandler<B> + 'static,
{
fn handle_event(&mut self, device: &mut DrmDevice<B>, frame: u32, duration: Duration,
userdata: Box<Any>) { userdata: Box<Any>) {
let id: Id = *userdata.downcast().unwrap(); let crtc_id: crtc::Handle = *userdata.downcast().unwrap();
if let Some(backend) = device.backends[id.raw()].upgrade() { let token = device.backends.get(&crtc_id).cloned();
if let Some(token) = token {
// we can now unlock the buffer // we can now unlock the buffer
backend.borrow().unlock_buffer(); (**self.evlh.state().get(&token)).unlock_buffer();
trace!(device.logger, "Handling event for backend {:?}", id.raw()); trace!(device.logger, "Handling event for backend {:?}", crtc_id);
// and then call the user to render the next frame // and then call the user to render the next frame
self.0.ready(self.1, id, frame, duration); self.handler
.ready(self.evlh, device, &token, frame, duration);
} }
} }
} }
@ -518,13 +529,17 @@ where
dev, dev,
2, 2,
None::<&mut ()>, None::<&mut ()>,
Some(&mut PageFlipHandler(handler, evlh)), Some(&mut PageFlipHandler {
handler,
evlh,
_marker: PhantomData,
}),
None::<&mut ()>, None::<&mut ()>,
).unwrap(); ).unwrap();
}, },
error: |evlh, id, _, error| { error: |evlh, id, _, error| {
warn!(id.0.logger, "DrmDevice errored: {}", error); warn!(id.0.logger, "DrmDevice errored: {}", error);
id.1.error(evlh, error); id.1.error(evlh, &mut id.0, error);
}, },
} }
} }

View File

@ -7,7 +7,6 @@ use glium::backend::{Backend, Context, Facade};
use glium::debug::DebugCallbackBehavior; use glium::debug::DebugCallbackBehavior;
use std::ops::Deref; use std::ops::Deref;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::rc::Rc; use std::rc::Rc;
impl From<SwapBuffersError> for GliumSwapBuffersError { impl From<SwapBuffersError> for GliumSwapBuffersError {
@ -69,15 +68,9 @@ impl<T: EGLGraphicsBackend> Facade for GliumGraphicsBackend<T> {
} }
} }
/// Converter trait to expose `glium` compatibility for all `EGLGraphicsBackend`s impl<T: EGLGraphicsBackend + 'static> From<T> for GliumGraphicsBackend<T> {
pub trait IntoGlium: EGLGraphicsBackend + Sized { fn from(backend: T) -> Self {
/// Wrap the given `EGLGraphicsBackend` to a `GliumGraphicBackend` GliumGraphicsBackend::new(backend)
fn into_glium(self) -> GliumGraphicsBackend<Self>;
}
impl<T: EGLGraphicsBackend + 'static> IntoGlium for T {
fn into_glium(self) -> GliumGraphicsBackend<Self> {
GliumGraphicsBackend::new(self)
} }
} }