Rework EGL module

This commit is contained in:
Drakulix 2017-12-21 16:01:16 +01:00
parent 5c846d550c
commit 5d7e96103d
19 changed files with 2051 additions and 2024 deletions

View File

@ -7,32 +7,31 @@ description = "Smithay is a library for writing wayland compositors."
repository = "https://github.com/Smithay/smithay" repository = "https://github.com/Smithay/smithay"
[dependencies] [dependencies]
wayland-server = "0.12.0" wayland-server = "0.12.4"
nix = "0.9.0" nix = "0.9.0"
xkbcommon = "0.2.1" xkbcommon = "0.2.1"
tempfile = "2.1.5" tempfile = "2.1.5"
slog = { version = "2.0.0" } slog = { version = "2.1.1" }
slog-stdlog = "2.0.0-0.2" slog-stdlog = "3.0.2"
libloading = "0.4.0" libloading = "0.4.0"
wayland-client = { version = "0.9.9", optional = true } wayland-client = { version = "0.12.4", optional = true }
winit = { version = "0.8.2", optional = true } winit = { version = "0.9.0", optional = true }
drm = { version = "^0.3.1", optional = true } drm = { version = "^0.3.1", optional = true }
gbm = { version = "^0.3.0", optional = true, default-features = false, features = ["drm-support"] } gbm = { version = "^0.4.0", git = "https://github.com/Smithay/gbm.rs.git", branch = "feature/ref_counted_api", optional = true, default-features = false, features = ["drm-support"] }
glium = { version = "0.17.1", optional = true, default-features = false } glium = { version = "0.19.0", optional = true, default-features = false }
input = { version = "0.4.0", optional = true } input = { version = "0.4.0", optional = true }
udev = { version = "0.2.0", optional = true } udev = { version = "0.2.0", optional = true }
rental = "0.4.11" wayland-protocols = { version = "0.12.4", features = ["unstable_protocols", "server"] }
wayland-protocols = { version = "0.12.0", features = ["unstable_protocols", "server"] }
image = "0.17.0" image = "0.17.0"
error-chain = "0.11.0" error-chain = "0.11.0"
lazy_static = "1.0.0" lazy_static = "1.0.0"
[build-dependencies] [build-dependencies]
gl_generator = "0.5" gl_generator = "0.7"
[dev-dependencies] [dev-dependencies]
slog-term = "2.0" slog-term = "2.3"
slog-async = "2.0" slog-async = "2.2"
rand = "0.3" rand = "0.3"
ctrlc = { version = "3.0", features = ["termination"] } ctrlc = { version = "3.0", features = ["termination"] }

View File

@ -1,7 +1,9 @@
use glium; use glium;
use glium::{Frame, Surface}; use glium::{Frame, Surface, GlObject};
use glium::backend::Facade;
use glium::index::PrimitiveType; use glium::index::PrimitiveType;
use smithay::backend::graphics::egl::EGLGraphicsBackend; use glium::texture::{MipmapsOption, UncompressedFloatFormat, Texture2d};
use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImage};
use smithay::backend::graphics::glium::GliumGraphicsBackend; use smithay::backend::graphics::glium::GliumGraphicsBackend;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::ops::Deref; use std::ops::Deref;
@ -106,58 +108,37 @@ impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> fo
} }
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> { impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
pub fn render_shm(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), pub fn texture_from_mem(&self, contents: &[u8], surface_dimensions: (u32, u32)) {
surface_location: (i32, i32), screen_size: (u32, u32)) {
let image = glium::texture::RawImage2d { let image = glium::texture::RawImage2d {
data: contents.into(), data: contents.into(),
width: surface_dimensions.0, width: surface_dimensions.0,
height: surface_dimensions.1, height: surface_dimensions.1,
format: glium::texture::ClientFormat::U8U8U8U8, format: glium::texture::ClientFormat::U8U8U8U8,
}; };
let opengl_texture = glium::texture::Texture2d::new(&self.display, image).unwrap(); let opengl_texture = Texture2d::new(&self.display, image).unwrap();
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
let yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
let x = 2.0 * (surface_location.0 as f32) / (screen_size.0 as f32) - 1.0;
let y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32);
let uniforms = uniform! {
matrix: [
[xscale, 0.0 , 0.0, 0.0],
[ 0.0 , yscale , 0.0, 0.0],
[ 0.0 , 0.0 , 1.0, 0.0],
[ x , y , 0.0, 1.0]
],
tex: &opengl_texture
};
target
.draw(
&self.vertex_buffer,
&self.index_buffer,
&self.program,
&uniforms,
&Default::default(),
)
.unwrap();
} }
pub fn render_egl(&self, target: &mut glium::Frame, images: Vec<EGLImage>, pub fn texture_from_egl(&self, image: EGLImage, format: UncompressedFloatFormat,
format: UncompressedFloatFormat, y_inverted: bool, surface_dimensions: (u32, u32), surface_dimensions: (u32, u32))
surface_location: (i32, i32), screen_size: (u32, u32)) -> Texture2d
{ {
let opengl_texture = glium::texture::Texture2d::empty_with_format( let opengl_texture = Texture2d::empty_with_format(
&self.display, &self.display,
MipmapsOption::NoMipmap,
format, format,
MipmapsOption::NoMipmap,
surface_dimensions.0, surface_dimensions.0,
surface_dimensions.1, surface_dimensions.1,
).unwrap(); ).unwrap();
self.display.exec_in_context(|| { self.display.get_context().exec_in_context(|| {
self.display.borrow().egl_image_to_texture(images[0], opengl_texture.get_id()); self.display.borrow().egl_image_to_texture(image, opengl_texture.get_id());
}); });
opengl_texture
}
pub fn render_texture(&self, target: &mut glium::Frame, texture: Texture2d,
y_inverted: bool, surface_dimensions: (u32, u32),
surface_location: (i32, i32), screen_size: (u32, u32))
{
let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32); let xscale = 2.0 * (surface_dimensions.0 as f32) / (screen_size.0 as f32);
let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32); let mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32);
if y_inverted { if y_inverted {
@ -174,7 +155,7 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
[ 0.0 , 0.0 , 1.0, 0.0], [ 0.0 , 0.0 , 1.0, 0.0],
[ x , y , 0.0, 1.0] [ x , y , 0.0, 1.0]
], ],
tex: &opengl_texture tex: &texture,
}; };
target target

View File

@ -1,11 +1,13 @@
use super::WindowMap; use super::{GliumDrawer, WindowMap};
use glium::texture::{Texture2d, UncompressedFloatFormat};
use rand; use rand;
use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImage};
use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes,
SurfaceUserImplementation}; SurfaceUserImplementation};
use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole,
ShellSurfaceUserImplementation, ToplevelConfigure}; ShellSurfaceUserImplementation, ToplevelConfigure};
use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents;
use smithay::wayland::drm::{with_buffer_contents as drm_buffer_contents, Attributes, EGLImage}; use smithay::wayland::drm::{with_buffer_contents as drm_buffer_contents, Attributes, Format};
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::{EventLoop, StateToken}; use wayland_server::{EventLoop, StateToken};
@ -14,7 +16,8 @@ define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] );
#[derive(Default)] #[derive(Default)]
pub struct SurfaceData { pub struct SurfaceData {
pub buffer: Option<(Vec<u8>, (u32, u32))>, pub texture: Option<Texture2d>,
pub buffer: Option<Buffer>,
} }
pub enum Buffer { pub enum Buffer {
@ -22,17 +25,34 @@ pub enum Buffer {
Shm { data: Vec<u8>, size: (u32, u32) }, Shm { data: Vec<u8>, size: (u32, u32) },
} }
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> { unsafe impl Send for Buffer {}
pub fn surface_implementation<G: EGLGraphicsBackend + 'static>() -> SurfaceUserImplementation<SurfaceData, Roles, GliumDrawer<G>> {
SurfaceUserImplementation { SurfaceUserImplementation {
commit: |_, _, surface, token| { commit: |_, drawer, surface, token| {
// we retrieve the contents of the associated buffer and copy it // we retrieve the contents of the associated buffer and copy it
token.with_surface_data(surface, |attributes| { token.with_surface_data(surface, |attributes| {
match attributes.buffer.take() { match attributes.buffer() {
Some(Some((buffer, (_x, _y)))) => { Some(ref buffer) => {
// we ignore hotspot coordinates in this simple example // we ignore hotspot coordinates in this simple example (`attributes.buffer_coordinates()`)
if let Ok(_) = drm_buffer_contents(&buffer, |attributes, images| { if drm_buffer_contents(&buffer, drawer.borrow(), |attributes, images| {
let format = match attributes.format {
Format::RGB => UncompressedFloatFormat::U8U8U8,
Format::RGBA => UncompressedFloatFormat::U8U8U8U8,
_ => {
// we don't handle the more complex formats here.
attributes.user_data.buffer = None;
attributes.user_data.texture = None;
return;
},
};
attributes.user_data.texture = Some(drawer.texture_from_egl(
images[0] /*Both simple formats only have one plane*/,
format,
(attributes.width, attributes.height)
));
attributes.user_data.buffer = Some(Buffer::Egl { images, attributes }); attributes.user_data.buffer = Some(Buffer::Egl { images, attributes });
}) {} else { }).is_err() {
shm_buffer_contents(&buffer, |slice, data| { shm_buffer_contents(&buffer, |slice, data| {
let offset = data.offset as usize; let offset = data.offset as usize;
let stride = data.stride as usize; let stride = data.stride as usize;
@ -43,17 +63,16 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
new_vec new_vec
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]); .extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
} }
attributes.user_data.texture = Some(drawer.texture_from_mem(&new_vec, data.width as u32, data.height as u32));
attributes.user_data.buffer = attributes.user_data.buffer =
Some(Buffer::Shm { data: new_vec, position: (data.width as u32, data.height as u32) }); Some(Buffer::Shm { data: new_vec, position: (data.width as u32, data.height as u32) });
}).unwrap(); }).unwrap();
} }
buffer.release();
} }
Some(None) => { None => {
// erase the contents // erase the contents
attributes.user_data.buffer = None; attributes.user_data.buffer = None;
} }
None => {}
} }
}); });
}, },
@ -126,14 +145,14 @@ pub type MyWindowMap = WindowMap<
fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>, fn(&SurfaceAttributes<SurfaceData>) -> Option<(i32, i32)>,
>; >;
pub fn init_shell( pub fn init_shell<ID: 'static>(
evl: &mut EventLoop, log: ::slog::Logger) evl: &mut EventLoop, log: ::slog::Logger, data: ID)
-> ( -> (
CompositorToken<SurfaceData, Roles, ()>, CompositorToken<SurfaceData, Roles, ID>,
StateToken<ShellState<SurfaceData, Roles, (), ()>>, StateToken<ShellState<SurfaceData, Roles, ID, ()>>,
Rc<RefCell<MyWindowMap>>, Rc<RefCell<MyWindowMap>>,
) { ) {
let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), (), log.clone()); let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone());
let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new( let window_map = Rc::new(RefCell::new(WindowMap::<_, _, _, (), _>::new(
compositor_token, compositor_token,

View File

@ -12,7 +12,6 @@ extern crate wayland_server;
mod helpers; mod helpers;
use glium::Surface; use glium::Surface;
use glium::texture::UncompressedFloatFormat;
use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer}; use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer};
use slog::{Drain, Logger}; use slog::{Drain, Logger};
use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::graphics::egl::EGLGraphicsBackend;
@ -137,13 +136,19 @@ fn main() {
let (mut display, mut event_loop) = wayland_server::create_display(); let (mut display, mut event_loop) = wayland_server::create_display();
if let Ok(_) = renderer.bind_wl_display(&display) {
info!(log, "EGL hardware-acceleration enabled");
}
let drawer = Rc::new(GliumDrawer::from(renderer));
/* /*
* Initialize the globals * Initialize the globals
*/ */
init_shm_global(&mut event_loop, vec![], log.clone()); init_shm_global(&mut event_loop, vec![], log.clone());
let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone()); let (compositor_token, _shell_state_token, window_map) = init_shell(&mut event_loop, log.clone(), drawer.clone());
let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone()); let (seat_token, _) = Seat::new(&mut event_loop, "winit".into(), log.clone());
@ -192,7 +197,6 @@ fn main() {
/* /*
* Initialize glium * Initialize glium
*/ */
let drawer = GliumDrawer::from(renderer);
input.set_handler(WinitInputHandler { input.set_handler(WinitInputHandler {
log: log.clone(), log: log.clone(),
@ -227,36 +231,23 @@ fn main() {
wl_surface, wl_surface,
initial_place, initial_place,
|_surface, attributes, role, &(mut x, mut y)| { |_surface, attributes, role, &(mut x, mut y)| {
if let Some(Buffer::Egl { images, attributes }) = attributes.user_data.buffer {
// there is actually something to draw ! // there is actually something to draw !
if let Some(texture) = attributes.user_data.texture {
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) { if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x; x += subdata.x;
y += subdata.y; y += subdata.y;
} }
drawer.render_egl( drawer.render_texture(
&mut frame, &mut frame,
images, texture,
format: match attributes.format { match *attributes.user_data.buffer.as_ref().unwrap() {
Format::RGB => UncompressedFloatFormat::U8U8U8, Buffer::Egl { ref attributes, .. } => attributes.y_inverted,
Format::RGBA => UncompressedFloatFormat::U8U8U8U8, Buffer::Shm { .. } => false,
_ => unimplemented!(), },
match *attributes.user_data.buffer.as_ref().unwrap() {
Buffer::Egl { ref attributes, .. } => (attributes.width, attributes.height),
Buffer::Shm { ref size, .. } => *size,
}, },
attributes.y_inverted,
(attributes.width, attributes.height),
(x, y),
screen_dimensions,
);
TraversalAction::DoChildren((x, y))
} else if let Some(Buffer::Shm { data, size: (w, h))) = attributes.user_data.buffer {
// there is actually something to draw !
if let Ok(subdata) = Role::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
drawer.render_shm(
&mut frame,
data,
(w, h),
(x, y), (x, y),
screen_dimensions, screen_dimensions,
); );

View File

@ -1,129 +1,61 @@
use super::devices;
use super::error::*; use super::error::*;
use super::DevPath;
use backend::graphics::GraphicsBackend; use backend::graphics::GraphicsBackend;
use backend::graphics::egl::{EGLGraphicsBackend, EGLSurface, PixelFormat, SwapBuffersError}; use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, EGLImage, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError};
use backend::graphics::egl::native::{Gbm, GbmSurfaceArguments};
use drm::control::{Device, ResourceInfo}; use drm::control::{Device, ResourceInfo};
use drm::control::{connector, crtc, encoder, framebuffer, Mode}; use drm::control::{connector, crtc, encoder, framebuffer, Mode};
use gbm::{BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle}; use gbm::{Device as GbmDevice, BufferObject, BufferObjectFlags, Format as GbmFormat, Surface as GbmSurface, SurfaceBufferHandle};
use image::{ImageBuffer, Rgba}; use image::{ImageBuffer, Rgba};
use nix::libc::c_void; use nix::libc::{c_void, c_uint};
use std::cell::Cell; use std::cell::Cell;
use std::rc::Rc; use std::rc::Rc;
use wayland_server::Display;
/*
Dependency graph
- drm
- gbm
- context
- gbm_surface
- egl_surface
- gbm_buffers
- cursor
*/
pub(crate) struct GbmTypes<'dev, 'context> {
cursor: Cell<BufferObject<'dev, ()>>,
surface: Surface<'context>,
}
pub(crate) struct EGL<'gbm, 'context> {
surface: EGLSurface<'context, 'gbm, GbmSurface<'context, framebuffer::Info>>,
buffers: GbmBuffers<'gbm>,
}
pub(crate) struct GbmBuffers<'gbm> {
front_buffer: Cell<SurfaceBufferHandle<'gbm, framebuffer::Info>>,
next_buffer: Cell<Option<SurfaceBufferHandle<'gbm, framebuffer::Info>>>,
}
rental! {
mod graphics {
use drm::control::framebuffer;
use gbm::Surface as GbmSurface;
use std::rc::Rc;
use super::devices::{Context, Context_Borrow};
use super::GbmTypes;
#[rental]
pub(crate) struct Surface<'context> {
gbm: Box<GbmSurface<'context, framebuffer::Info>>,
egl: super::EGL<'gbm, 'context>,
}
#[rental]
pub(crate) struct Graphics {
#[subrental(arity = 3)]
context: Rc<Context>,
gbm: GbmTypes<'context_1, 'context_2>,
}
}
}
use self::graphics::{Graphics, Surface};
/// Backend based on a `DrmDevice` and a given crtc /// Backend based on a `DrmDevice` and a given crtc
pub struct DrmBackend { pub struct DrmBackend<A: Device + 'static> {
graphics: Graphics, context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
surface: EGLSurface<GbmSurface<framebuffer::Info>>,
cursor: Cell<BufferObject<()>>,
front_buffer: Cell<SurfaceBufferHandle<framebuffer::Info>>,
next_buffer: Cell<Option<SurfaceBufferHandle<framebuffer::Info>>>,
crtc: crtc::Handle, crtc: crtc::Handle,
mode: Mode, mode: Mode,
connectors: Vec<connector::Handle>, connectors: Vec<connector::Handle>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl DrmBackend { impl<A: Device + 'static> DrmBackend<A> {
pub(crate) fn new( pub(crate) fn new(
context: Rc<devices::Context>, crtc: crtc::Handle, mode: Mode, connectors: Vec<connector::Handle>, context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
crtc: crtc::Handle,
mode: Mode,
connectors: Vec<connector::Handle>,
logger: ::slog::Logger, logger: ::slog::Logger,
) -> Result<DrmBackend> { ) -> Result<Self> {
// 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 (w, h) = mode.size(); let (w, h) = mode.size();
Ok(DrmBackend { debug!(log, "Creating Surface");
graphics: Graphics::try_new(context, |context| { let surface = context.create_surface(
Ok(GbmTypes { GbmSurfaceArguments {
cursor: { size: (w as u32, h as u32),
// Create an unused cursor buffer (we don't want an Option here) format: GbmFormat::XRGB8888,
Cell::new(context flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
.devices }
.gbm ).chain_err(|| ErrorKind::GbmInitFailed)?;
.create_buffer_object(
1,
1,
GbmFormat::ARGB8888,
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
)
.chain_err(|| ErrorKind::GbmInitFailed)?)
},
surface: Surface::try_new(
{
debug!(log, "Creating GbmSurface");
// create a gbm surface
Box::new(context
.devices
.gbm
.create_surface(
w as u32,
h as u32,
GbmFormat::XRGB8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.chain_err(|| ErrorKind::GbmInitFailed)?)
},
|surface| {
// create an egl surface from the gbm one
debug!(log, "Creating EGLSurface");
let egl_surface = context.egl.create_surface(surface)?;
// make it active for the first `crtc::set` // make it active for the first `crtc::set`
// (which is needed before the first page_flip) // (which is needed before the first page_flip)
unsafe { unsafe {
egl_surface surface
.make_current() .make_current()
.chain_err(|| ErrorKind::FailedToSwap)? .chain_err(|| ErrorKind::FailedToSwap)?
}; };
egl_surface surface
.swap_buffers() .swap_buffers()
.chain_err(|| ErrorKind::FailedToSwap)?; .chain_err(|| ErrorKind::FailedToSwap)?;
@ -132,54 +64,51 @@ 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!(log, "FrontBuffer color format: {:?}", front_bo.format()); debug!(log, "FrontBuffer color format: {:?}", front_bo.format());
// we need a framebuffer per front buffer
let fb = framebuffer::create(context.devices.drm, &*front_bo) // we need a framebuffer for the front buffer
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?; let fb = framebuffer::create(&*context, &*front_bo)
.chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", context.dev_path())))?;
debug!(log, "Initialize screen"); debug!(log, "Initialize screen");
crtc::set( crtc::set(
context.devices.drm, &*context,
crtc, crtc,
fb.handle(), fb.handle(),
&connectors, &connectors,
(0, 0), (0, 0),
Some(mode), Some(mode),
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", context.devices.drm)))?; ).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", crtc, context.dev_path())))?;
front_bo.set_userdata(fb); front_bo.set_userdata(fb).unwrap();
Ok(EGL { let cursor = Cell::new(context.create_buffer_object(
surface: egl_surface, 1, 1,
buffers: GbmBuffers { GbmFormat::ARGB8888,
BufferObjectFlags::CURSOR | BufferObjectFlags::WRITE,
).chain_err(|| ErrorKind::GbmInitFailed)?);
Ok(DrmBackend {
context,
surface,
cursor,
front_buffer: Cell::new(front_bo), front_buffer: Cell::new(front_bo),
next_buffer: Cell::new(None), next_buffer: Cell::new(None),
},
})
},
).map_err(Error::from)?,
})
})?,
crtc, crtc,
mode, mode,
connectors, connectors,
logger: log.clone(), logger: log,
}) })
} }
pub(crate) fn unlock_buffer(&self) { pub(crate) 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
self.graphics.rent(|gbm| { if let Some(next_buffer) = self.next_buffer.replace(None) {
gbm.surface.rent(|egl| {
let next_bo = egl.buffers.next_buffer.replace(None);
if let Some(next_buffer) = next_bo {
trace!(self.logger, "Releasing old front buffer"); trace!(self.logger, "Releasing old front buffer");
egl.buffers.front_buffer.set(next_buffer); self.front_buffer.set(next_buffer);
// drop and release the old buffer // drop and release the old buffer
} }
})
});
} }
/// Add a connector to backend /// Add a connector to backend
@ -188,8 +117,8 @@ impl DrmBackend {
/// ///
/// 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 = connector::Info::load_from_device(self.graphics.head().head().head(), connector) let info = connector::Info::load_from_device(&*self.context, connector)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?; .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))?;
// check if the connector can handle the current mode // check if the connector can handle the current mode
if info.modes().contains(&self.mode) { if info.modes().contains(&self.mode) {
@ -197,18 +126,15 @@ impl DrmBackend {
let encoders = info.encoders() let encoders = info.encoders()
.iter() .iter()
.map(|encoder| { .map(|encoder| {
encoder::Info::load_from_device(self.graphics.head().head().head(), *encoder) encoder::Info::load_from_device(&*self.context, *encoder)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))) .chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.context.dev_path())))
}) })
.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
let resource_handles = self.graphics let resource_handles = self.context
.head()
.head()
.head()
.resource_handles() .resource_handles()
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))?; .chain_err(|| ErrorKind::DrmDev(format!("Error loading resources on {:?}", self.context.dev_path())))?;
if !encoders if !encoders
.iter() .iter()
.map(|encoder| encoder.possible_crtcs()) .map(|encoder| encoder.possible_crtcs())
@ -239,7 +165,7 @@ impl DrmBackend {
/// 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) = connector::Info::load_from_device(self.graphics.head().head().head(), connector) { if let Ok(info) = connector::Info::load_from_device(&*self.context, connector) {
info!( info!(
self.logger, self.logger,
"Removing connector: {:?}", "Removing connector: {:?}",
@ -262,8 +188,8 @@ impl DrmBackend {
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.connectors { for connector in &self.connectors {
if !connector::Info::load_from_device(self.graphics.head().head().head(), *connector) if !connector::Info::load_from_device(&*self.context, *connector)
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())))? .chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.context.dev_path())))?
.modes() .modes()
.contains(&mode) .contains(&mode)
{ {
@ -271,90 +197,82 @@ impl DrmBackend {
} }
} }
// borrow & clone stuff because rust cannot figure out the upcoming info!(self.logger, "Setting new mode: {:?}", mode.name());
// closure otherwise.
let crtc = self.crtc;
let connectors_ref = &self.connectors;
let logger_ref = &self.logger;
let (w, h) = mode.size(); let (w, h) = mode.size();
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!( debug!(self.logger, "Reinitializing surface for new mode: {}:{}", w, h);
logger_ref, let surface = self.context.create_surface(
"Reinitializing surface for new mode: {}:{}", w, h GbmSurfaceArguments {
); size: (w as u32, h as u32),
graphics.gbm.surface = Surface::try_new( format: GbmFormat::XRGB8888,
{ flags: BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
// create a new gbm surface }
debug!(logger_ref, "Creating GbmSurface"); ).chain_err(|| ErrorKind::GbmInitFailed)?;
Box::new(graphics
.context
.devices
.gbm
.create_surface(
w as u32,
h as u32,
GbmFormat::XRGB8888,
BufferObjectFlags::SCANOUT | BufferObjectFlags::RENDERING,
)
.chain_err(|| ErrorKind::GbmInitFailed)?)
},
|surface| {
// create an egl surface from the gbm one
debug!(logger_ref, "Creating EGLSurface");
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`
// (which is needed before the first page_flip) // (which is needed before the first page_flip)
unsafe { unsafe {
egl_surface surface
.make_current() .make_current()
.chain_err(|| ErrorKind::FailedToSwap)? .chain_err(|| ErrorKind::FailedToSwap)?
}; };
egl_surface surface
.swap_buffers() .swap_buffers()
.chain_err(|| ErrorKind::FailedToSwap)?; .chain_err(|| ErrorKind::FailedToSwap)?;
let mut front_bo = surface // Clean up next_buffer
.lock_front_buffer() {
.chain_err(|| ErrorKind::FailedToSwap)?; if let Some(mut old_bo) = self.next_buffer.take() {
debug!( if let Ok(Some(fb)) = old_bo.take_userdata() {
logger_ref, if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) {
"FrontBuffer color format: {:?}", warn!(self.logger, "Error releasing old back_buffer framebuffer: {:?}", err);
front_bo.format() }
); }
// we need a framebuffer per front_buffer }
let fb = framebuffer::create(graphics.context.devices.drm, &*front_bo) }
.chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?;
debug!(logger_ref, "Initialize screen"); // Cleanup front_buffer and init the first screen on the new front_buffer
// (must be done before calling page_flip for the first time)
let fb = {
let mut old_front_bo = self.front_buffer.replace(
surface
.lock_front_buffer()
.chain_err(|| ErrorKind::FailedToSwap)?
);
if let Ok(Some(fb)) = old_front_bo.take_userdata() {
if let Err(err) = framebuffer::destroy(&*self.context, fb.handle()) {
warn!(self.logger, "Error releasing old front_buffer framebuffer: {:?}", err);
}
}
let front_bo = self.front_buffer.get_mut();
debug!(self.logger, "FrontBuffer color format: {:?}", front_bo.format());
// we also need a new framebuffer for the front buffer
let dev_path = self.context.dev_path();
let fb = framebuffer::create(&*self.context, &**front_bo)
.chain_err(|| ErrorKind::DrmDev(format!("Error creating framebuffer on {:?}", dev_path)))?;
front_bo.set_userdata(fb).unwrap();
fb
};
debug!(self.logger, "Setting screen");
crtc::set( crtc::set(
graphics.context.devices.drm, &*self.context,
crtc, self.crtc,
fb.handle(), fb.handle(),
connectors_ref, &self.connectors,
(0, 0), (0, 0),
Some(mode), Some(mode),
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", graphics.context.devices.drm)))?; ).chain_err(|| ErrorKind::DrmDev(format!("Error setting crtc {:?} on {:?}", self.crtc, self.context.dev_path())))?;
front_bo.set_userdata(fb);
Ok(EGL {
surface: egl_surface,
buffers: GbmBuffers {
front_buffer: Cell::new(front_bo),
next_buffer: Cell::new(None),
},
})
},
)?;
Ok(()) // Drop the old surface after cleanup
})?; self.surface = surface;
info!(self.logger, "Setting new mode: {:?}", mode.name());
self.mode = mode; self.mode = mode;
Ok(()) Ok(())
} }
@ -365,66 +283,55 @@ impl DrmBackend {
} }
} }
impl Drop for DrmBackend { impl<A: Device + 'static> Drop for DrmBackend<A> {
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)
let crtc = self.crtc; if let Ok(Some(fb)) = {
self.graphics.rent_all_mut(|graphics| { if let Some(mut next) = self.next_buffer.take() {
if let Some(fb) = graphics.gbm.surface.rent(|egl| {
if let Some(mut next) = egl.buffers.next_buffer.take() {
next.take_userdata() next.take_userdata()
} else if let Ok(mut next) = graphics.gbm.surface.head().lock_front_buffer() { } else if let Ok(mut next) = self.surface.lock_front_buffer() {
next.take_userdata() next.take_userdata()
} else { } else {
None Ok(None)
} }
}) { } {
// ignore failure at this point // ignore failure at this point
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle()); let _ = framebuffer::destroy(&*self.context, fb.handle());
} }
if let Some(fb) = graphics.gbm.surface.rent_mut(|egl| { if let Ok(Some(fb)) = self.front_buffer.get_mut().take_userdata() {
let first = egl.buffers.front_buffer.get_mut();
first.take_userdata()
}) {
// ignore failure at this point // ignore failure at this point
let _ = framebuffer::destroy(&*graphics.context.devices.drm, fb.handle()); let _ = framebuffer::destroy(&*self.context, fb.handle());
} }
// ignore failure at this point // ignore failure at this point
let _ = crtc::clear_cursor(&*graphics.context.devices.drm, crtc); let _ = crtc::clear_cursor(&*self.context, self.crtc);
})
} }
} }
impl GraphicsBackend for DrmBackend { impl<A: Device + 'static> GraphicsBackend for DrmBackend<A> {
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>; type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
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.logger, "Move the cursor to {},{}", x, y); trace!(self.logger, "Move the cursor to {},{}", x, y);
crtc::move_cursor( crtc::move_cursor(
self.graphics.head().head().head(), &*self.context,
self.crtc, self.crtc,
(x as i32, y as i32), (x as i32, y as i32),
).chain_err(|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head()))) ).chain_err(|| ErrorKind::DrmDev(format!("Error moving cursor on {:?}", self.context.dev_path())))
} }
fn set_cursor_representation( fn set_cursor_representation(
&self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32) &self, buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>, hotspot: (u32, u32)
) -> Result<()> { ) -> Result<()> {
let (w, h) = buffer.dimensions(); let (w, h) = buffer.dimensions();
debug!(self.logger, "Importing cursor"); debug!(self.logger, "Importing cursor");
self.graphics.rent_all(|graphics| -> Result<()> {
graphics.gbm.cursor.set({
// import the cursor into a buffer we can render // import the cursor into a buffer we can render
let mut cursor = graphics let mut cursor = self
.context .context
.devices
.gbm
.create_buffer_object( .create_buffer_object(
w, w,
h, h,
@ -434,88 +341,80 @@ impl GraphicsBackend for DrmBackend {
.chain_err(|| ErrorKind::GbmInitFailed)?; .chain_err(|| ErrorKind::GbmInitFailed)?;
cursor cursor
.write(&**buffer) .write(&**buffer)
.chain_err(|| ErrorKind::GbmInitFailed)?
.chain_err(|| ErrorKind::GbmInitFailed)?; .chain_err(|| ErrorKind::GbmInitFailed)?;
trace!(self.logger, "Set the new imported cursor"); trace!(self.logger, "Setting the new imported cursor");
// and set it // and set it
if crtc::set_cursor2( if crtc::set_cursor2(
self.graphics.head().head().head(), &*self.context,
self.crtc, self.crtc,
&cursor, &cursor,
(hotspot.0 as i32, hotspot.1 as i32), (hotspot.0 as i32, hotspot.1 as i32),
).is_err() ).is_err()
{ {
crtc::set_cursor(self.graphics.head().head().head(), self.crtc, &cursor).chain_err( crtc::set_cursor(&*self.context, self.crtc, &cursor).chain_err(
|| ErrorKind::DrmDev(format!("{:?}", self.graphics.head().head().head())), || ErrorKind::DrmDev(format!("Failed to set cursor on {:?}", self.context.dev_path())),
)?; )?;
} }
// and store it // and store it
cursor self.cursor.set(cursor);
});
Ok(()) Ok(())
})
} }
} }
impl EGLGraphicsBackend for DrmBackend { impl<A: Device + 'static> EGLGraphicsBackend for DrmBackend<A> {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
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 {
let next = egl.buffers.next_buffer.take(); let nb = self.next_buffer.take();
let res = next.is_some(); let res = nb.is_some();
egl.buffers.next_buffer.set(next); self.next_buffer.set(nb);
res res
}) { } {
warn!(self.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);
} }
// flip normally // flip normally
graphics.gbm.surface.rent(|egl| egl.surface.swap_buffers())?; self.surface.swap_buffers()?;
graphics.gbm.surface.rent_all(|surface| { // supporting only one buffer would cause a lot of inconvinience and
// supporting this error would cause a lot of inconvinience and
// would most likely result in a lot of flickering. // would most likely result in a lot of flickering.
// neither weston, wlc or wlroots bother with that as well. // neither weston, wlc or wlroots bother with that as well.
// so we just assume we got at least two buffers to do flipping // so we just assume we got at least two buffers to do flipping.
let mut next_bo = surface let mut next_bo = self.surface
.gbm
.lock_front_buffer() .lock_front_buffer()
.expect("Surface only has one front buffer. Not supported by smithay"); .expect("Surface only has one front buffer. Not supported by smithay");
// create a framebuffer if the front buffer does not have one already // create a framebuffer if the front buffer does not have one already
// (they are reused by gbm) // (they are reused by gbm)
let maybe_fb = next_bo.userdata().cloned(); let maybe_fb = next_bo.userdata().map_err(|_| SwapBuffersError::ContextLost)?.cloned();
let fb = if let Some(info) = maybe_fb { let fb = if let Some(info) = maybe_fb {
info info
} else { } else {
let fb = framebuffer::create(graphics.context.devices.drm, &*next_bo) let fb = framebuffer::create(&*self.context, &*next_bo)
.map_err(|_| SwapBuffersError::ContextLost)?; .map_err(|_| SwapBuffersError::ContextLost)?;
next_bo.set_userdata(fb); next_bo.set_userdata(fb).unwrap();
fb fb
}; };
surface.egl.buffers.next_buffer.set(Some(next_bo)); self.next_buffer.set(Some(next_bo));
trace!(self.logger, "Queueing Page flip"); trace!(self.logger, "Queueing Page flip");
// and flip // and flip
crtc::page_flip( crtc::page_flip(
graphics.context.devices.drm, &*self.context,
self.crtc, self.crtc,
fb.handle(), fb.handle(),
&[crtc::PageFlipFlags::PageFlipEvent], &[crtc::PageFlipFlags::PageFlipEvent],
).map_err(|_| SwapBuffersError::ContextLost) ).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.graphics self.context.get_proc_address(symbol)
.head()
.rent(|context| context.get_proc_address(symbol))
} }
fn get_framebuffer_dimensions(&self) -> (u32, u32) { fn get_framebuffer_dimensions(&self) -> (u32, u32) {
@ -524,19 +423,27 @@ impl EGLGraphicsBackend for DrmBackend {
} }
fn is_current(&self) -> bool { fn is_current(&self) -> bool {
self.graphics.rent_all(|graphics| { self.context.is_current() && self.surface.is_current()
graphics.context.egl.is_current() && graphics.gbm.surface.rent(|egl| egl.surface.is_current())
})
} }
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
self.graphics self.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.graphics self.context.get_pixel_format()
.head()
.rent(|context| context.get_pixel_format())
} }
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
self.context.bind_wl_display(display).map_err(EglExtensionNotSupportedError::from)
}
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
self.context.unbind_wl_display(display).map_err(EglExtensionNotSupportedError::from)
}
/*unsafe fn egl_image_to_texture(&self, image: EGLImage, tex_id: c_uint) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
self.graphics.head().rent(|context| context.egl_image_to_texture(image, tex_id))?;
Ok(())
}*/
} }

View File

@ -2,9 +2,8 @@
//! Errors thrown by the `DrmDevice` and `DrmBackend` //! Errors thrown by the `DrmDevice` and `DrmBackend`
//! //!
use backend::graphics::egl; use backend::graphics::egl::error as egl;
use drm::control::{connector, crtc, Mode}; use drm::control::{connector, crtc, Mode};
use rental::TryNewError;
error_chain! { error_chain! {
errors { errors {
@ -60,9 +59,3 @@ error_chain! {
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"]; EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"];
} }
} }
impl<H> From<TryNewError<Error, H>> for Error {
fn from(err: TryNewError<Error, H>) -> Error {
err.0
}
}

View File

@ -187,26 +187,27 @@
//! # } //! # }
//! ``` //! ```
use backend::graphics::egl::{EGLContext, GlAttributes, PixelFormatRequirements}; #[cfg(feature = "backend_session")]
use backend::graphics::egl::EGLGraphicsBackend; use backend::graphics::egl::EGLGraphicsBackend;
use backend::graphics::egl::context::{EGLContext, GlAttributes, PixelFormatRequirements};
use backend::graphics::egl::native::Gbm;
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
use backend::session::SessionObserver; use backend::session::SessionObserver;
use drm::Device as BasicDevice; use drm::Device as BasicDevice;
use drm::control::{connector, crtc, encoder, Mode, ResourceInfo}; use drm::control::{connector, crtc, encoder, Mode, ResourceInfo};
use drm::control::Device as ControlDevice; use drm::control::Device as ControlDevice;
use drm::result::Error as DrmError; use drm::result::Error as DrmError;
use drm::control::framebuffer;
use gbm::Device as GbmDevice; use gbm::Device as GbmDevice;
use nix; use nix;
use nix::Result as NixResult;
use nix::unistd::close;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::io::Result as IoResult; use std::io::Result as IoResult;
use std::mem; use std::os::unix::io::{AsRawFd, RawFd};
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
use std::rc::Rc; use std::rc::Rc;
use std::sync::{Once, ONCE_INIT};
use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use wayland_server::{EventLoopHandle, StateProxy, StateToken}; use wayland_server::{EventLoopHandle, StateProxy, StateToken};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
@ -217,76 +218,28 @@ pub mod error;
pub use self::backend::DrmBackend; pub use self::backend::DrmBackend;
use self::error::*; use self::error::*;
/// Internal struct as required by the drm crate static LOAD: Once = ONCE_INIT;
#[derive(Debug)]
pub(crate) struct DrmDev(RawFd);
impl AsRawFd for DrmDev {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl BasicDevice for DrmDev {}
impl ControlDevice for DrmDev {}
impl DrmDev {
unsafe fn new_from_fd(fd: RawFd) -> Self {
DrmDev(fd)
}
fn new_from_file(file: File) -> Self {
DrmDev(file.into_raw_fd())
}
}
rental! {
mod devices {
use drm::control::framebuffer;
use gbm::{Device as GbmDevice, Surface as GbmSurface};
use ::backend::graphics::egl::EGLContext;
use super::DrmDev;
#[rental]
pub(crate) struct Context {
#[subrental(arity = 2)]
devices: Box<Devices>,
egl: EGLContext<'devices_1, GbmSurface<'devices_1, framebuffer::Info>>,
}
#[rental]
pub(crate) struct Devices {
drm: Box<DrmDev>,
gbm: GbmDevice<'drm>,
}
}
}
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<B: Borrow<DrmBackend> + 'static> { pub struct DrmDevice<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> {
context: Rc<Context>, context: Rc<EGLContext<Gbm<framebuffer::Info>, GbmDevice<A>>>,
backends: HashMap<crtc::Handle, StateToken<B>>, backends: HashMap<crtc::Handle, StateToken<B>>,
old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>, old_state: HashMap<crtc::Handle, (crtc::Info, Vec<connector::Handle>)>,
active: bool, active: bool,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> { impl<A: ControlDevice + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static> DrmDevice<A, B> {
/// Create a new `DrmDevice` from a raw file descriptor /// Create a new `DrmDevice` from an open drm node
/// ///
/// Returns an error of opening the device failed or context creation was not /// Returns an error if the file is no valid drm node or context creation was not
/// successful. /// successful.
/// pub fn new<L>(dev: A, logger: L) -> Result<Self>
/// # Safety
/// The file descriptor might not be valid and needs to be owned by smithay,
/// make sure not to share it. Otherwise undefined behavior might occur.
pub unsafe fn new_from_fd<L>(fd: RawFd, logger: L) -> Result<Self>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
DrmDevice::new( DrmDevice::new_with_gl_attr(
DrmDev::new_from_fd(fd), dev,
GlAttributes { GlAttributes {
version: None, version: None,
profile: None, profile: None,
@ -297,53 +250,11 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
) )
} }
/// Create a new `DrmDevice` from a raw file descriptor and given `GlAttributes` /// Create a new `DrmDevice` from an open drm node and given `GlAttributes`
///
/// Returns an error of opening the device failed or context creation was not
/// successful.
///
/// # Safety
/// The file descriptor might not be valid and needs to be owned by smithay,
/// make sure not to share it. Otherwise undefined behavior might occur.
pub unsafe fn new_from_fd_with_gl_attr<L>(fd: RawFd, attributes: GlAttributes, logger: L) -> Result<Self>
where
L: Into<Option<::slog::Logger>>,
{
DrmDevice::new(DrmDev::new_from_fd(fd), attributes, logger)
}
/// Create a new `DrmDevice` from a `File` of an open drm node
/// ///
/// Returns an error if the file is no valid drm node or context creation was not /// Returns an error if the file is no valid drm node or context creation was not
/// successful. /// successful.
pub fn new_from_file<L>(file: File, logger: L) -> Result<Self> pub fn new_with_gl_attr<L>(dev: A, attributes: GlAttributes, logger: L) -> Result<Self>
where
L: Into<Option<::slog::Logger>>,
{
DrmDevice::new(
DrmDev::new_from_file(file),
GlAttributes {
version: None,
profile: None,
debug: cfg!(debug_assertions),
vsync: true,
},
logger,
)
}
/// Create a new `DrmDevice` from a `File` of an open drm node and given `GlAttributes`
///
/// Returns an error if the file is no valid drm node or context creation was not
/// successful.
pub fn new_from_file_with_gl_attr<L>(file: File, attributes: GlAttributes, logger: L) -> Result<Self>
where
L: Into<Option<::slog::Logger>>,
{
DrmDevice::new(DrmDev::new_from_file(file), attributes, logger)
}
fn new<L>(drm: DrmDev, attributes: GlAttributes, logger: L) -> Result<Self>
where where
L: Into<Option<::slog::Logger>>, L: Into<Option<::slog::Logger>>,
{ {
@ -355,50 +266,22 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
* and loading the DRI driver fails. * and loading the DRI driver fails.
* Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL. * Workaround this by dlopen()'ing libglapi with RTLD_GLOBAL.
*/ */
unsafe { LOAD.call_once(|| unsafe {
nix::libc::dlopen( nix::libc::dlopen(
"libglapi.so.0".as_ptr() as *const _, "libglapi.so.0".as_ptr() as *const _,
nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL, nix::libc::RTLD_LAZY | nix::libc::RTLD_GLOBAL,
); );
} });
info!(log, "DrmDevice initializing");
// we want to mode-set, so we better be the master
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
let mut old_state = HashMap::new();
let res_handles = drm.resource_handles()
.chain_err(|| ErrorKind::DrmDev(format!("Loading drm resources on {:?}", drm)))?;
for &con in res_handles.connectors() {
let con_info = connector::Info::load_from_device(&drm, con)
.chain_err(|| ErrorKind::DrmDev(format!("Loading connector info on {:?}", drm)))?;
if let Some(enc) = con_info.current_encoder() {
let enc_info = encoder::Info::load_from_device(&drm, enc)
.chain_err(|| ErrorKind::DrmDev(format!("Loading encoder info on {:?}", drm)))?;
if let Some(crtc) = enc_info.current_crtc() {
let info = crtc::Info::load_from_device(&drm, crtc)
.chain_err(|| ErrorKind::DrmDev(format!("Loading crtc info on {:?}", drm)))?;
old_state
.entry(crtc)
.or_insert((info, Vec::new()))
.1
.push(con);
}
}
}
let mut drm = DrmDevice {
// Open the gbm device from the drm device and create a context based on that // Open the gbm device from the drm device and create a context based on that
Ok(DrmDevice { context: Rc::new(EGLContext::new(
context: Rc::new(Context::try_new( {
Box::new(Devices::try_new(Box::new(drm), |drm| {
debug!(log, "Creating gbm device"); debug!(log, "Creating gbm device");
GbmDevice::new_from_drm(drm).chain_err(|| ErrorKind::GbmInitFailed) let gbm = GbmDevice::new(dev).chain_err(|| ErrorKind::GbmInitFailed)?;
})?),
|devices| {
debug!(log, "Creating egl context from gbm device"); debug!(log, "Creating egl context from gbm device");
EGLContext::new_from_gbm( gbm
devices.gbm, },
attributes, attributes,
PixelFormatRequirements { PixelFormatRequirements {
hardware_accelerated: Some(true), hardware_accelerated: Some(true),
@ -407,14 +290,39 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
..Default::default() ..Default::default()
}, },
log.clone(), log.clone(),
).map_err(Error::from) ).map_err(Error::from)?),
},
)?),
backends: HashMap::new(), backends: HashMap::new(),
old_state, old_state: HashMap::new(),
active: true, active: true,
logger: log, logger: log.clone(),
}) };
info!(log, "DrmDevice initializing");
// we want to mode-set, so we better be the master
drm.set_master().chain_err(|| ErrorKind::DrmMasterFailed)?;
let res_handles = drm.resource_handles()
.chain_err(|| ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", drm.dev_path())))?;
for &con in res_handles.connectors() {
let con_info = connector::Info::load_from_device(&drm, con)
.chain_err(|| ErrorKind::DrmDev(format!("Error loading connector info on {:?}", drm.dev_path())))?;
if let Some(enc) = con_info.current_encoder() {
let enc_info = encoder::Info::load_from_device(&drm, enc)
.chain_err(|| ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", drm.dev_path())))?;
if let Some(crtc) = enc_info.current_crtc() {
let info = crtc::Info::load_from_device(&drm, crtc)
.chain_err(|| ErrorKind::DrmDev(format!("Error loading crtc info on {:?}", drm.dev_path())))?;
drm.old_state
.entry(crtc)
.or_insert((info, Vec::new()))
.1
.push(con);
}
}
}
Ok(drm)
} }
/// Create a new backend on a given crtc with a given `Mode` for a given amount /// Create a new backend on a given crtc with a given `Mode` for a given amount
@ -442,12 +350,9 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
// check if we have an encoder for every connector and the mode mode // check if we have an encoder for every connector and the mode mode
for connector in &connectors { for connector in &connectors {
let con_info = connector::Info::load_from_device(self.context.head().head(), *connector) let con_info = connector::Info::load_from_device(self, *connector)
.chain_err(|| { .chain_err(|| {
ErrorKind::DrmDev(format!( ErrorKind::DrmDev(format!("Error loading connector info on {:?}", self.dev_path()))
"Loading connector info on {:?}",
self.context.head().head()
))
})?; })?;
// check the mode // check the mode
@ -460,21 +365,15 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
.encoders() .encoders()
.iter() .iter()
.map(|encoder| { .map(|encoder| {
encoder::Info::load_from_device(self.context.head().head(), *encoder).chain_err(|| { encoder::Info::load_from_device(self, *encoder).chain_err(|| {
ErrorKind::DrmDev(format!( ErrorKind::DrmDev(format!("Error loading encoder info on {:?}", self.dev_path()))
"Loading encoder info on {:?}",
self.context.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
let resource_handles = self.resource_handles().chain_err(|| { let resource_handles = self.resource_handles().chain_err(|| {
ErrorKind::DrmDev(format!( ErrorKind::DrmDev(format!("Error loading drm resources on {:?}", self.dev_path()))
"Loading drm resources on {:?}",
self.context.head().head()
))
})?; })?;
if !encoders if !encoders
.iter() .iter()
@ -517,37 +416,38 @@ impl<B: From<DrmBackend> + Borrow<DrmBackend> + 'static> DrmDevice<B> {
state.into().remove(token); state.into().remove(token);
} }
} }
}
/// Close the device pub trait DevPath {
/// fn dev_path(&self) -> Option<PathBuf>;
/// ## Warning }
/// Never call this function if the device is managed by another backend e.g. the `UdevBackend`.
/// Only use this function for manually initialized devices. impl<A: AsRawFd> DevPath for A {
pub fn close(self) -> NixResult<()> { fn dev_path(&self) -> Option<PathBuf> {
let fd = self.as_raw_fd(); use std::fs;
mem::drop(self);
close(fd) fs::read_link(format!("/proc/self/fd/{:?}", self.as_raw_fd())).ok()
} }
} }
// for users convinience and FdEventSource registering // for users convinience and FdEventSource registering
impl<B: Borrow<DrmBackend> + 'static> AsRawFd for DrmDevice<B> { impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> AsRawFd for DrmDevice<A, B> {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.context.head().head().as_raw_fd() self.context.as_raw_fd()
} }
} }
impl<B: Borrow<DrmBackend> + 'static> BasicDevice for DrmDevice<B> {} impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> BasicDevice for DrmDevice<A, B> {}
impl<B: Borrow<DrmBackend> + 'static> ControlDevice for DrmDevice<B> {} impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> ControlDevice for DrmDevice<A, B> {}
impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> { impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> Drop for DrmDevice<A, B> {
fn drop(&mut self) { fn drop(&mut self) {
if Rc::strong_count(&self.context) > 1 { if Rc::strong_count(&self.context) > 1 {
panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed"); panic!("Pending DrmBackends. Please free all backends before the DrmDevice gets destroyed");
} }
for (handle, (info, connectors)) in self.old_state.drain() { for (handle, (info, connectors)) in self.old_state.drain() {
if let Err(err) = crtc::set( if let Err(err) = crtc::set(
self.context.head().head(), &*self.context,
handle, handle,
info.fb(), info.fb(),
&connectors, &connectors,
@ -569,7 +469,7 @@ impl<B: Borrow<DrmBackend> + 'static> Drop for DrmDevice<B> {
} }
} }
impl<B: Borrow<DrmBackend> + 'static> Hash for DrmDevice<B> { impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> Hash for DrmDevice<A, B> {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.as_raw_fd().hash(state) self.as_raw_fd().hash(state)
} }
@ -578,7 +478,7 @@ impl<B: Borrow<DrmBackend> + 'static> Hash 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<B: Borrow<DrmBackend> + 'static> { pub trait DrmHandler<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + '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.
/// ///
@ -589,7 +489,7 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
/// The device is already borrowed from the given `state`. Borrowing it again will panic /// The device is already borrowed from the given `state`. Borrowing it again will panic
/// and is not necessary as it is already provided via the `device` parameter. /// and is not necessary as it is already provided via the `device` parameter.
fn ready<'a, S: Into<StateProxy<'a>>>( fn ready<'a, S: Into<StateProxy<'a>>>(
&mut self, state: S, device: &mut DrmDevice<B>, backend: &StateToken<B>, crtc: crtc::Handle, &mut self, state: S, device: &mut DrmDevice<A, B>, backend: &StateToken<B>, crtc: crtc::Handle,
frame: u32, duration: Duration, frame: u32, duration: Duration,
); );
/// The `DrmDevice` has thrown an error. /// The `DrmDevice` has thrown an error.
@ -600,18 +500,19 @@ pub trait DrmHandler<B: Borrow<DrmBackend> + 'static> {
/// ## Panics /// ## Panics
/// The device is already borrowed from the given `state`. Borrowing it again will panic /// The device is already borrowed from the given `state`. Borrowing it again will panic
/// and is not necessary as it is already provided via the `device` parameter. /// and is not necessary as it is already provided via the `device` parameter.
fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>, error: DrmError); fn error<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<A, B>, error: DrmError);
} }
/// 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<B, H>( pub fn drm_device_bind<A, B, H>(
evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<B>>, handler: H evlh: &mut EventLoopHandle, device: StateToken<DrmDevice<A, B>>, handler: H
) -> IoResult<FdEventSource<(StateToken<DrmDevice<B>>, H)>> ) -> IoResult<FdEventSource<(StateToken<DrmDevice<A, B>>, H)>>
where where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static, A: ControlDevice + 'static,
H: DrmHandler<B> + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static,
H: DrmHandler<A, B> + 'static,
{ {
let fd = evlh.state().get(&device).as_raw_fd(); let fd = evlh.state().get(&device).as_raw_fd();
evlh.add_fd_event_source( evlh.add_fd_event_source(
@ -622,10 +523,11 @@ where
) )
} }
fn fd_event_source_implementation<B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<B>>, H)> fn fd_event_source_implementation<A, B, H>() -> FdEventSourceImpl<(StateToken<DrmDevice<A, B>>, H)>
where where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static, A: ControlDevice + 'static,
H: DrmHandler<B> + 'static, B: From<DrmBackend<A>> + Borrow<DrmBackend<A>> + 'static,
H: DrmHandler<A, B> + 'static,
{ {
FdEventSourceImpl { FdEventSourceImpl {
ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| { ready: |evlh, &mut (ref mut dev_token, ref mut handler), _, _| {
@ -674,9 +576,9 @@ where
} }
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
impl<B: Borrow<DrmBackend> + 'static> SessionObserver for StateToken<DrmDevice<B>> { impl<A: ControlDevice + 'static, B: Borrow<DrmBackend<A>> + 'static> SessionObserver for StateToken<DrmDevice<A, B>> {
fn pause<'a>(&mut self, state: &mut StateProxy<'a>) { fn pause<'a>(&mut self, state: &mut StateProxy<'a>) {
let device: &mut DrmDevice<B> = state.get_mut(self); let device: &mut DrmDevice<A, B> = state.get_mut(self);
device.active = false; device.active = false;
if let Err(err) = device.drop_master() { if let Err(err) = device.drop_master() {
error!( error!(

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,622 @@
use super::{ffi, EGLSurface, PixelFormat};
use super::error::*;
use super::native;
#[cfg(feature = "backend_drm")]
use gbm::{Device as GbmDevice};
#[cfg(feature = "backend_drm")]
use drm::{Device as BasicDevice};
#[cfg(feature = "backend_drm")]
use drm::control::{Device as ControlDevice};
use nix::libc::{c_void, c_int};
use slog;
use std::ffi::{CString, CStr};
use std::marker::PhantomData;
use std::rc::Rc;
use std::ptr;
use std::mem;
use std::ops::{Deref, DerefMut};
#[cfg(feature = "backend_drm")]
use std::os::unix::io::{AsRawFd, RawFd};
use wayland_server::Display;
/// EGL context for rendering
pub struct EGLContext<B: native::Backend, N: native::NativeDisplay<B>> {
native: N,
pub(crate) context: Rc<ffi::egl::types::EGLContext>,
pub(crate) display: Rc<ffi::egl::types::EGLDisplay>,
pub(crate) config_id: ffi::egl::types::EGLConfig,
pub(crate) surface_attributes: Vec<c_int>,
pixel_format: PixelFormat,
wl_drm_support: bool,
egl_to_texture_support: bool,
logger: slog::Logger,
_backend: PhantomData<B>,
}
impl<B: native::Backend, N: native::NativeDisplay<B>> Deref for EGLContext<B, N> {
type Target = N;
fn deref(&self) -> &N {
&self.native
}
}
impl<B: native::Backend, N: native::NativeDisplay<B>> DerefMut for EGLContext<B, N> {
fn deref_mut(&mut self) -> &mut N {
&mut self.native
}
}
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
pub fn new<L>(
native: N,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
logger: L
) -> Result<EGLContext<B, N>>
where
L: Into<Option<::slog::Logger>>
{
let log = ::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
let ptr = native.ptr()?;
let (
context,
display,
config_id,
surface_attributes,
pixel_format,
wl_drm_support,
egl_to_texture_support,
) = unsafe { EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) }?;
Ok(EGLContext {
native,
context,
display,
config_id,
surface_attributes,
pixel_format,
wl_drm_support,
egl_to_texture_support,
logger: log,
_backend: PhantomData,
})
}
unsafe fn new_internal(
ptr: ffi::NativeDisplayType,
mut attributes: GlAttributes,
reqs: PixelFormatRequirements,
log: ::slog::Logger,
) -> Result<(
Rc<ffi::egl::types::EGLContext>,
Rc<ffi::egl::types::EGLDisplay>,
ffi::egl::types::EGLConfig,
Vec<c_int>,
PixelFormat,
bool,
bool,
)>
{
// If no version is given, try OpenGLES 3.0, if available,
// fallback to 2.0 otherwise
let version = match attributes.version {
Some((3, x)) => (3, x),
Some((2, x)) => (2, x),
None => {
debug!(log, "Trying to initialize EGL with OpenGLES 3.0");
attributes.version = Some((3, 0));
match EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log.clone()) {
Ok(x) => return Ok(x),
Err(err) => {
warn!(log, "EGL OpenGLES 3.0 Initialization failed with {}", err);
debug!(log, "Trying to initialize EGL with OpenGLES 2.0");
attributes.version = Some((2, 0));
return EGLContext::<B, N>::new_internal(ptr, attributes, reqs, log);
}
}
}
Some((1, x)) => {
error!(
log,
"OpenGLES 1.* is not supported by the EGL renderer backend"
);
bail!(ErrorKind::OpenGlVersionNotSupported((1, x)));
}
Some(version) => {
error!(
log,
"OpenGLES {:?} is unknown and not supported by the EGL renderer backend", version
);
bail!(ErrorKind::OpenGlVersionNotSupported(version));
}
};
ffi::egl::LOAD.call_once(|| {
ffi::egl::load_with(|sym| {
let name = CString::new(sym).unwrap();
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
match symbol {
Ok(x) => *x as *const _,
Err(_) => ptr::null(),
}
});
ffi::gl::load_with(|sym| {
let name = CString::new(sym).unwrap();
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
match symbol {
Ok(x) => *x as *const _,
Err(_) => ptr::null(),
}
});
});
// the first step is to query the list of extensions without any display, if supported
let dp_extensions = {
let p = ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32);
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
// `eglQueryString` returns an error
if p.is_null() {
vec![]
} else {
let p = CStr::from_ptr(p);
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
}
};
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions);
let display = B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone());
if display == ffi::egl::NO_DISPLAY {
error!(log, "EGL Display is not valid");
bail!(ErrorKind::DisplayNotSupported);
}
let egl_version = {
let mut major: ffi::egl::types::EGLint = mem::uninitialized();
let mut minor: ffi::egl::types::EGLint = mem::uninitialized();
if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
bail!(ErrorKind::InitFailed);
}
info!(log, "EGL Initialized");
info!(log, "EGL Version: {:?}", (major, minor));
(major, minor)
};
// the list of extensions supported by the client once initialized is different from the
// list of extensions obtained earlier
let extensions = if egl_version >= (1, 2) {
let p = CStr::from_ptr(ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32));
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
} else {
vec![]
};
info!(log, "EGL Extensions: {:?}", extensions);
if egl_version >= (1, 2) && ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) == 0 {
error!(
log,
"OpenGLES not supported by the underlying EGL implementation"
);
bail!(ErrorKind::OpenGlesNotSupported);
}
let descriptor = {
let mut out: Vec<c_int> = Vec::with_capacity(37);
if egl_version >= (1, 2) {
trace!(log, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
out.push(ffi::egl::RGB_BUFFER as c_int);
}
trace!(log, "Setting SURFACE_TYPE to WINDOW");
out.push(ffi::egl::SURFACE_TYPE as c_int);
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
out.push((ffi::egl::WINDOW_BIT) as c_int);
match version {
(3, _) => {
if egl_version < (1, 3) {
error!(
log,
"OpenglES 3.* is not supported on EGL Versions lower then 1.3"
);
bail!(ErrorKind::NoAvailablePixelFormat);
}
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES3");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
trace!(log, "Setting CONFORMANT to OPENGL_ES3");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
}
(2, _) => {
if egl_version < (1, 3) {
error!(
log,
"OpenglES 2.* is not supported on EGL Versions lower then 1.3"
);
bail!(ErrorKind::NoAvailablePixelFormat);
}
trace!(log, "Setting RENDERABLE_TYPE to OPENGL_ES2");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
trace!(log, "Setting CONFORMANT to OPENGL_ES2");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
}
(_, _) => unreachable!(),
};
if let Some(hardware_accelerated) = reqs.hardware_accelerated {
out.push(ffi::egl::CONFIG_CAVEAT as c_int);
out.push(if hardware_accelerated {
trace!(log, "Setting CONFIG_CAVEAT to NONE");
ffi::egl::NONE as c_int
} else {
trace!(log, "Setting CONFIG_CAVEAT to SLOW_CONFIG");
ffi::egl::SLOW_CONFIG as c_int
});
}
if let Some(color) = reqs.color_bits {
trace!(log, "Setting RED_SIZE to {}", color / 3);
out.push(ffi::egl::RED_SIZE as c_int);
out.push((color / 3) as c_int);
trace!(
log,
"Setting GREEN_SIZE to {}",
color / 3 + if color % 3 != 0 { 1 } else { 0 }
);
out.push(ffi::egl::GREEN_SIZE as c_int);
out.push((color / 3 + if color % 3 != 0 { 1 } else { 0 }) as c_int);
trace!(
log,
"Setting BLUE_SIZE to {}",
color / 3 + if color % 3 == 2 { 1 } else { 0 }
);
out.push(ffi::egl::BLUE_SIZE as c_int);
out.push((color / 3 + if color % 3 == 2 { 1 } else { 0 }) as c_int);
}
if let Some(alpha) = reqs.alpha_bits {
trace!(log, "Setting ALPHA_SIZE to {}", alpha);
out.push(ffi::egl::ALPHA_SIZE as c_int);
out.push(alpha as c_int);
}
if let Some(depth) = reqs.depth_bits {
trace!(log, "Setting DEPTH_SIZE to {}", depth);
out.push(ffi::egl::DEPTH_SIZE as c_int);
out.push(depth as c_int);
}
if let Some(stencil) = reqs.stencil_bits {
trace!(log, "Setting STENCIL_SIZE to {}", stencil);
out.push(ffi::egl::STENCIL_SIZE as c_int);
out.push(stencil as c_int);
}
if let Some(multisampling) = reqs.multisampling {
trace!(log, "Setting SAMPLES to {}", multisampling);
out.push(ffi::egl::SAMPLES as c_int);
out.push(multisampling as c_int);
}
if reqs.stereoscopy {
error!(log, "Stereoscopy is currently unsupported (sorry!)");
bail!(ErrorKind::NoAvailablePixelFormat);
}
out.push(ffi::egl::NONE as c_int);
out
};
// calling `eglChooseConfig`
let mut config_id = mem::uninitialized();
let mut num_configs = mem::uninitialized();
if ffi::egl::ChooseConfig(
display,
descriptor.as_ptr(),
&mut config_id,
1,
&mut num_configs,
) == 0
{
bail!(ErrorKind::ConfigFailed);
}
if num_configs == 0 {
error!(log, "No matching color format found");
bail!(ErrorKind::NoAvailablePixelFormat);
}
// analyzing each config
macro_rules! attrib {
($display:expr, $config:expr, $attr:expr) => (
{
let mut value = mem::uninitialized();
let res = ffi::egl::GetConfigAttrib($display, $config,
$attr as ffi::egl::types::EGLint, &mut value);
if res == 0 {
bail!(ErrorKind::ConfigFailed);
}
value
}
)
};
let desc = PixelFormat {
hardware_accelerated: attrib!(display, config_id, ffi::egl::CONFIG_CAVEAT)
!= ffi::egl::SLOW_CONFIG as i32,
color_bits: attrib!(display, config_id, ffi::egl::RED_SIZE) as u8
+ attrib!(display, config_id, ffi::egl::BLUE_SIZE) as u8
+ attrib!(display, config_id, ffi::egl::GREEN_SIZE) as u8,
alpha_bits: attrib!(display, config_id, ffi::egl::ALPHA_SIZE) as u8,
depth_bits: attrib!(display, config_id, ffi::egl::DEPTH_SIZE) as u8,
stencil_bits: attrib!(display, config_id, ffi::egl::STENCIL_SIZE) as u8,
stereoscopy: false,
double_buffer: true,
multisampling: match attrib!(display, config_id, ffi::egl::SAMPLES) {
0 | 1 => None,
a => Some(a as u16),
},
srgb: false, // TODO: use EGL_KHR_gl_colorspace to know that
};
info!(log, "Selected color format: {:?}", desc);
let mut context_attributes = Vec::with_capacity(10);
if egl_version >= (1, 5) || extensions.iter().any(|s| *s == "EGL_KHR_create_context") {
trace!(log, "Setting CONTEXT_MAJOR_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_MAJOR_VERSION as i32);
context_attributes.push(version.0 as i32);
trace!(log, "Setting CONTEXT_MINOR_VERSION to {}", version.1);
context_attributes.push(ffi::egl::CONTEXT_MINOR_VERSION as i32);
context_attributes.push(version.1 as i32);
if attributes.debug && egl_version >= (1, 5) {
trace!(log, "Setting CONTEXT_OPENGL_DEBUG to TRUE");
context_attributes.push(ffi::egl::CONTEXT_OPENGL_DEBUG as i32);
context_attributes.push(ffi::egl::TRUE as i32);
}
context_attributes.push(ffi::egl::CONTEXT_FLAGS_KHR as i32);
context_attributes.push(0);
} else if egl_version >= (1, 3) {
trace!(log, "Setting CONTEXT_CLIENT_VERSION to {}", version.0);
context_attributes.push(ffi::egl::CONTEXT_CLIENT_VERSION as i32);
context_attributes.push(version.0 as i32);
}
context_attributes.push(ffi::egl::NONE as i32);
trace!(log, "Creating EGL context...");
let context = ffi::egl::CreateContext(display, config_id, ptr::null(), context_attributes.as_ptr());
if context.is_null() {
match ffi::egl::GetError() as u32 {
ffi::egl::BAD_ATTRIBUTE => bail!(ErrorKind::CreationFailed),
err_no => bail!(ErrorKind::Unknown(err_no)),
}
}
debug!(log, "EGL context successfully created");
let surface_attributes = {
let mut out: Vec<c_int> = Vec::with_capacity(3);
match reqs.double_buffer {
Some(true) => {
trace!(log, "Setting RENDER_BUFFER to BACK_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::BACK_BUFFER as c_int);
}
Some(false) => {
trace!(log, "Setting RENDER_BUFFER to SINGLE_BUFFER");
out.push(ffi::egl::RENDER_BUFFER as c_int);
out.push(ffi::egl::SINGLE_BUFFER as c_int);
}
None => {}
}
out.push(ffi::egl::NONE as i32);
out
};
info!(log, "EGL context created");
Ok((
Rc::new(context as *const _),
Rc::new(display as *const _),
config_id,
surface_attributes,
desc,
extensions.iter().any(|s| *s == "EGL_WL_bind_wayland_display"),
extensions.iter().any(|s| *s == "EGL_OES_image" || *s == "EGL_OES_image_base"),
))
}
/// Creates a surface for rendering
pub fn create_surface(&self, args: N::Arguments) -> Result<EGLSurface<B::Surface>> {
trace!(self.logger, "Creating EGL window surface.");
let res = EGLSurface::new(self, self.native.create_surface(args).chain_err(|| ErrorKind::SurfaceCreationFailed)?);
if res.is_ok() {
debug!(self.logger, "EGL surface successfully created");
}
res
}
/// Returns the address of an OpenGL function.
///
/// Supposes that the context has been made current before this function is called.
pub unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
let addr = CString::new(symbol.as_bytes()).unwrap();
let addr = addr.as_ptr();
ffi::egl::GetProcAddress(addr) as *const _
}
/// Returns true if the OpenGL context is the current one in the thread.
pub fn is_current(&self) -> bool {
unsafe { ffi::egl::GetCurrentContext() == (*self.context) as *const _ }
}
/// Returns the pixel format of the main framebuffer of the context.
pub fn get_pixel_format(&self) -> PixelFormat {
self.pixel_format
}
/// Binds this EGL context to the given Wayland display.
///
/// This will allow clients to utilize EGL to create hardware-accelerated
/// surfaces. The server will need to be able to handle egl-wl_buffers.
/// See the `wayland::drm` module.
///
/// ## Errors
///
/// This might return `WlExtensionNotSupported` if binding is not supported
/// by the EGL implementation.
///
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
/// `Display` multiple times, as only one context may be bound at any given time.
pub fn bind_wl_display(&self, display: &Display) -> Result<()> {
if !self.wl_drm_support {
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
}
let res = unsafe { ffi::egl::BindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
if res == 0 {
bail!(ErrorKind::OtherEGLDisplayAlreadyBound);
}
Ok(())
}
/// Unbinds this EGL context from the given Wayland display.
///
/// This will stop clients from using previously available extensions
/// to utilize hardware-accelerated surface via EGL.
///
/// ## Errors
///
/// This might return `WlExtensionNotSupported` if binding is not supported
/// by the EGL implementation.
///
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
/// `Display` multiple times, as only one context may be bound at any given time.
pub fn unbind_wl_display(&self, display: &Display) -> Result<()> {
if !self.wl_drm_support {
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
}
let res = unsafe { ffi::egl::UnbindWaylandDisplayWL(*self.display, display.ptr() as *mut _) };
if res == 0 {
bail!(ErrorKind::NoEGLDisplayBound);
}
Ok(())
}
/*
pub unsafe fn egl_image_to_texture(&self, image: ffi::egl::types::EGLImage, tex_id: c_uint) -> Result<()> {
if !self.egl_to_texture_support {
bail!(ErrorKind::EglExtensionNotSupported(&["EGL_OES_image", "EGL_OES_image_base"]));
}
ffi::gl::EGLImageTargetTexture2DOES(tex_id, image);
Ok(())
}
pub unsafe fn destroy_egl_image(&self, image: ffi::egl::types::EGLImage, tex_id: c_uint) -> Result<()> {
ffi::gl::DestroyImageKHR(self.display, image);
Ok(())
}
*/
}
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Send> Send for EGLContext<B, N> {}
unsafe impl<B: native::Backend, N: native::NativeDisplay<B> + Sync> Sync for EGLContext<B, N> {}
impl<B: native::Backend, N: native::NativeDisplay<B>> Drop for EGLContext<B, N> {
fn drop(&mut self) {
unsafe {
// we don't call MakeCurrent(0, 0) because we are not sure that the context
// is still the current one
ffi::egl::DestroyContext((*self.display) as *const _, (*self.context) as *const _);
ffi::egl::Terminate((*self.display) as *const _);
}
}
}
#[cfg(feature = "backend_drm")]
impl<T: 'static, A: AsRawFd + 'static> AsRawFd for EGLContext<native::Gbm<T>, GbmDevice<A>> {
fn as_raw_fd(&self) -> RawFd {
self.native.as_raw_fd()
}
}
#[cfg(feature = "backend_drm")]
impl<T: 'static, A: BasicDevice + 'static> BasicDevice for EGLContext<native::Gbm<T>, GbmDevice<A>> {}
#[cfg(feature = "backend_drm")]
impl<T: 'static, A: ControlDevice + 'static> ControlDevice for EGLContext<native::Gbm<T>, GbmDevice<A>> {}
/// Attributes to use when creating an OpenGL context.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GlAttributes {
/// Describes the OpenGL API and version that are being requested when a context is created.
///
/// `Some(3, 0)` will request a OpenGL ES 3.0 context for example.
/// `None` means "don't care" (minimum will be 2.0).
pub version: Option<(u8, u8)>,
/// OpenGL profile to use
pub profile: Option<GlProfile>,
/// Whether to enable the debug flag of the context.
///
/// Debug contexts are usually slower but give better error reporting.
pub debug: bool,
/// Whether to use vsync. If vsync is enabled, calling swap_buffers will block until the screen refreshes.
/// This is typically used to prevent screen tearing.
pub vsync: bool,
}
/// Describes the requested OpenGL context profiles.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GlProfile {
/// Include all the immediate more functions and definitions.
Compatibility,
/// Include all the future-compatible functions and definitions.
Core,
}
/// Describes how the backend should choose a pixel format.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct PixelFormatRequirements {
/// If `true`, only hardware-accelerated formats will be conisdered. If `false`, only software renderers.
/// `None` means "don't care". Default is `None`.
pub hardware_accelerated: Option<bool>,
/// Minimum number of bits for the color buffer, excluding alpha. None means "don't care". The default is `None``.
pub color_bits: Option<u8>,
/// If `true`, the color buffer must be in a floating point format. Default is `false`.
///
/// Using floating points allows you to write values outside of the `[0.0, 1.0]` range.
pub float_color_buffer: bool,
/// Minimum number of bits for the alpha in the color buffer. `None` means "don't care". The default is `None`.
pub alpha_bits: Option<u8>,
/// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
pub depth_bits: Option<u8>,
/// Minimum number of bits for the depth buffer. `None` means "don't care". The default value is `None`.
pub stencil_bits: Option<u8>,
/// If `true`, only double-buffered formats will be considered. If `false`, only single-buffer formats.
/// `None` means "don't care". The default is `None`.
pub double_buffer: Option<bool>,
/// Contains the minimum number of samples per pixel in the color, depth and stencil buffers.
/// `None` means "don't care". Default is `None`. A value of `Some(0)` indicates that multisampling must not be enabled.
pub multisampling: Option<u16>,
/// If `true`, only stereoscopic formats will be considered. If `false`, only non-stereoscopic formats.
/// The default is `false`.
pub stereoscopy: bool,
}

View File

@ -0,0 +1,70 @@
error_chain! {
errors {
#[doc = "The requested OpenGL version is not supported"]
OpenGlVersionNotSupported(version: (u8, u8)) {
description("The requested OpenGL version is not supported."),
display("The requested OpenGL version {:?} is not supported.", version),
}
#[doc = "The EGL implementation does not support creating OpenGL ES contexts"]
OpenGlesNotSupported {
description("The EGL implementation does not support creating OpenGL ES contexts")
}
#[doc = "No available pixel format matched the criteria"]
NoAvailablePixelFormat {
description("No available pixel format matched the criteria.")
}
#[doc = "Backend does not match the context type"]
NonMatchingBackend(expected: &'static str) {
description("The expected backend did not match the runtime."),
display("The expected backend '{:?}' does not match the runtime.", expected),
}
#[doc = "EGL was unable to optain a valid EGL Display"]
DisplayNotSupported {
description("EGL was unable to optain a valid EGL Display")
}
#[doc = "eglInitialize returned an error"]
InitFailed {
description("Failed to initialize EGL")
}
#[doc = "Failed to configure the EGL context"]
ConfigFailed {
description("Failed to configure the EGL context")
}
#[doc = "Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements"]
CreationFailed {
description("Context creation failed as one or more requirements could not be met. Try removing some gl attributes or pixel format requirements")
}
#[doc = "eglCreateWindowSurface failed"]
SurfaceCreationFailed {
description("Failed to create a new EGLSurface")
}
#[doc = "The required EGL extension is not supported by the underlying EGL implementation"]
EglExtensionNotSupported(extensions: &'static [&'static str]) {
description("The required EGL extension is not supported by the underlying EGL implementation"),
display("None of the following EGL extensions is supported by the underlying EGL implementation,
at least one is required: {:?}", extensions)
}
#[doc = "Only one EGLDisplay may be bound to a given WlDisplay at any time"]
OtherEGLDisplayAlreadyBound {
description("Only one EGLDisplay may be bound to a given WlDisplay at any time")
}
#[doc = "No EGLDisplay is currently bound to this WlDisplay"]
NoEGLDisplayBound {
description("No EGLDisplay is currently bound to this WlDisplay")
}
#[doc = "The reason of failure could not be determined"]
Unknown(err_no: u32)
}
}

View File

@ -0,0 +1,144 @@
#![allow(missing_docs)]
pub use nix::libc::{c_long, c_void, c_uint, int32_t, uint64_t};
pub type khronos_utime_nanoseconds_t = khronos_uint64_t;
pub type khronos_uint64_t = uint64_t;
pub type khronos_ssize_t = c_long;
pub type EGLint = int32_t;
pub type EGLNativeDisplayType = NativeDisplayType;
pub type EGLNativePixmapType = NativePixmapType;
pub type EGLNativeWindowType = NativeWindowType;
pub type NativeDisplayType = *const c_void;
pub type NativePixmapType = *const c_void;
pub type NativeWindowType = *const c_void;
pub mod gl {
include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}
pub mod egl {
use super::*;
use libloading::Library;
use std::sync::{Once, ONCE_INIT};
lazy_static! {
pub static ref LIB: Library = {
Library::new("libEGL.so.1").expect("Failed to load LibEGL")
};
}
pub static LOAD: Once = ONCE_INIT;
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
/*
* `gl_generator` cannot generate bindings for the `EGL_WL_bind_wayland_display` extension.
* Lets do it ourselves...
*/
#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
pub unsafe fn BindWaylandDisplayWL(dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void) -> types::EGLBoolean {
__gl_imports::mem::transmute::<_, extern "system" fn(types::EGLDisplay, *mut __gl_imports::raw::c_void) -> types::EGLBoolean>(wayland_storage::BindWaylandDisplayWL.f)(dpy, display)
}
#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
pub unsafe fn UnbindWaylandDisplayWL(dpy: types::EGLDisplay, display: *mut __gl_imports::raw::c_void) -> types::EGLBoolean {
__gl_imports::mem::transmute::<_, extern "system" fn(types::EGLDisplay, *mut __gl_imports::raw::c_void) -> types::EGLBoolean>(wayland_storage::UnbindWaylandDisplayWL.f)(dpy, display)
}
#[allow(non_snake_case, unused_variables, dead_code)] #[inline]
pub unsafe fn QueryWaylandBufferWL(dpy: types::EGLDisplay, buffer: *mut __gl_imports::raw::c_void, attribute: types::EGLint, value: *mut types::EGLint) -> types::EGLBoolean {
__gl_imports::mem::transmute::<_, extern "system" fn(types::EGLDisplay, *mut __gl_imports::raw::c_void, types::EGLint, *mut types::EGLint) -> types::EGLBoolean>(wayland_storage::QueryWaylandBufferWL.f)(dpy, buffer, attribute, value)
}
mod wayland_storage {
use super::__gl_imports::raw;
use super::FnPtr;
pub static mut BindWaylandDisplayWL: FnPtr = FnPtr {
f: super::missing_fn_panic as *const raw::c_void,
is_loaded: false
};
pub static mut UnbindWaylandDisplayWL: FnPtr = FnPtr {
f: super::missing_fn_panic as *const raw::c_void,
is_loaded: false
};
pub static mut QueryWaylandBufferWL: FnPtr = FnPtr {
f: super::missing_fn_panic as *const raw::c_void,
is_loaded: false
};
}
#[allow(non_snake_case)]
pub mod BindWaylandDisplayWL {
use super::{wayland_storage, metaloadfn};
use super::__gl_imports::raw;
use super::FnPtr;
#[inline]
#[allow(dead_code)]
pub fn is_loaded() -> bool {
unsafe { wayland_storage::BindWaylandDisplayWL.is_loaded }
}
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&str) -> *const raw::c_void {
unsafe {
wayland_storage::BindWaylandDisplayWL = FnPtr::new(metaloadfn(&mut loadfn, "eglBindWaylandDisplayWL", &[]))
}
}
}
#[allow(non_snake_case)]
pub mod UnbindWaylandDisplayWL {
use super::{wayland_storage, metaloadfn};
use super::__gl_imports::raw;
use super::FnPtr;
#[inline]
#[allow(dead_code)]
pub fn is_loaded() -> bool {
unsafe { wayland_storage::UnbindWaylandDisplayWL.is_loaded }
}
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&str) -> *const raw::c_void {
unsafe {
wayland_storage::UnbindWaylandDisplayWL = FnPtr::new(metaloadfn(&mut loadfn, "eglUnbindWaylandDisplayWL", &[]))
}
}
}
#[allow(non_snake_case)]
pub mod QueryWaylandBufferWL {
use super::{wayland_storage, metaloadfn};
use super::__gl_imports::raw;
use super::FnPtr;
#[inline]
#[allow(dead_code)]
pub fn is_loaded() -> bool {
unsafe { wayland_storage::QueryWaylandBufferWL.is_loaded }
}
#[allow(dead_code)]
pub fn load_with<F>(mut loadfn: F) where F: FnMut(&str) -> *const raw::c_void {
unsafe {
wayland_storage::QueryWaylandBufferWL = FnPtr::new(metaloadfn(&mut loadfn, "eglQueryWaylandBufferWL", &[]))
}
}
}
// Accepted as <target> in eglCreateImageKHR
pub const WAYLAND_BUFFER_WL: c_uint = 0x31D5;
// Accepted in the <attrib_list> parameter of eglCreateImageKHR:
pub const WAYLAND_PLANE_WL: c_uint = 0x31D6;
// Possible values for EGL_TEXTURE_FORMAT:
pub const TEXTURE_Y_U_V_WL: int32_t = 0x31D7;
pub const TEXTURE_Y_UV_WL: int32_t = 0x31D8;
pub const TEXTURE_Y_XUXV_WL: int32_t = 0x31D9;
pub const TEXTURE_EXTERNAL_WL: int32_t = 0x31DA;
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
pub const EGL_TEXTURE_FORMAT: int32_t = 0x3080;
pub const WAYLAND_Y_INVERTED_WL: int32_t = 0x31DB;
}

View File

@ -0,0 +1,184 @@
//! Common traits and types for egl context creation and rendering
/// Large parts of this module are taken from
/// https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/src/api/egl/
///
/// It therefore falls under glutin's Apache 2.0 license
/// (see https://github.com/tomaka/glutin/tree/044e651edf67a2029eecc650dd42546af1501414/LICENSE)
use super::GraphicsBackend;
use nix::libc::c_void;
use std::fmt;
use wayland_server::Display;
pub use self::ffi::egl::types::EGLImage;
pub mod context;
pub use self::context::EGLContext;
pub mod error;
#[allow(non_camel_case_types, dead_code, unused_mut, non_upper_case_globals)]
pub mod ffi;
pub mod native;
pub mod surface;
pub use self::surface::EGLSurface;
/// Error that can happen when swapping buffers.
#[derive(Debug, Clone, PartialEq)]
pub enum SwapBuffersError {
/// The OpenGL context has been lost and needs to be recreated.
///
/// All the objects associated to it (textures, buffers, programs, etc.)
/// need to be recreated from scratch.
///
/// Operations will have no effect. Functions that read textures, buffers, etc.
/// from OpenGL will return uninitialized data instead.
///
/// A context loss usually happens on mobile devices when the user puts the
/// application on sleep and wakes it up later. However any OpenGL implementation
/// can theoretically lose the context at any time.
ContextLost,
/// The buffers have already been swapped.
///
/// This error can be returned when `swap_buffers` has been called multiple times
/// without any modification in between.
AlreadySwapped,
/// Unknown GL error
Unknown(u32),
}
impl fmt::Display for SwapBuffersError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
use std::error::Error;
write!(formatter, "{}", self.description())
}
}
impl ::std::error::Error for SwapBuffersError {
fn description(&self) -> &str {
match *self {
SwapBuffersError::ContextLost => "The context has been lost, it needs to be recreated",
SwapBuffersError::AlreadySwapped => {
"Buffers are already swapped, swap_buffers was called too many times"
}
SwapBuffersError::Unknown(_) => "Unknown Open GL error occurred",
}
}
fn cause(&self) -> Option<&::std::error::Error> {
None
}
}
/// Error that can happen on optional EGL features
#[derive(Debug, Clone, PartialEq)]
pub struct EglExtensionNotSupportedError(&'static [&'static str]);
impl fmt::Display for EglExtensionNotSupportedError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
write!(formatter, "None of the following EGL extensions is supported by the underlying EGL implementation,
at least one is required: {:?}", self.0)
}
}
impl ::std::error::Error for EglExtensionNotSupportedError {
fn description(&self) -> &str {
"The required EGL extension is not supported by the underlying EGL implementation"
}
fn cause(&self) -> Option<&::std::error::Error> {
None
}
}
impl From<error::Error> for EglExtensionNotSupportedError {
fn from(error: error::Error) -> Self {
match *error.kind() {
error::ErrorKind::EglExtensionNotSupported(extensions) => EglExtensionNotSupportedError(extensions),
_ => panic!("Error not convertible"),
}
}
}
/// Describes the pixel format of the main framebuffer
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormat {
/// is the format hardware accelerated
pub hardware_accelerated: bool,
/// number of bits used for colors
pub color_bits: u8,
/// number of bits used for alpha channel
pub alpha_bits: u8,
/// number of bits used for depth channel
pub depth_bits: u8,
/// number of bits used for stencil buffer
pub stencil_bits: u8,
/// is stereoscopy enabled
pub stereoscopy: bool,
/// is double buffering enabled
pub double_buffer: bool,
/// number of samples used for multisampling if enabled
pub multisampling: Option<u16>,
/// is srgb enabled
pub srgb: bool,
}
/// Trait that describes objects that have an OpenGL context
/// and can be used to render upon
pub trait EGLGraphicsBackend: GraphicsBackend {
/// Swaps buffers at the end of a frame.
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError>;
/// Returns the address of an OpenGL function.
///
/// Supposes that the context has been made current before this function is called.
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void;
/// Returns the dimensions of the window, or screen, etc in points.
///
/// That are the scaled pixels of the underlying graphics backend.
/// For nested compositors this will respect the scaling of the root compositor.
/// For drawing directly onto hardware this unit will be equal to actual pixels.
fn get_framebuffer_dimensions(&self) -> (u32, u32);
/// Returns true if the OpenGL context is the current one in the thread.
fn is_current(&self) -> bool;
/// Makes the OpenGL context the current context in the current thread.
///
/// # Unsafety
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError>;
/// Returns the pixel format of the main framebuffer of the context.
fn get_pixel_format(&self) -> PixelFormat;
/// Binds this EGL context to the given Wayland display.
///
/// This will allow clients to utilize EGL to create hardware-accelerated
/// surfaces. The server will need to be able to handle egl-wl_buffers.
/// See the `wayland::drm` module.
///
/// ## Errors
///
/// This might return `WlExtensionNotSupported` if binding is not supported
/// by the EGL implementation.
///
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
/// `Display` multiple times, as only one context may be bound at any given time.
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>;
/// Unbinds this EGL context from the given Wayland display.
///
/// This will stop clients from using previously available extensions
/// to utilize hardware-accelerated surface via EGL.
///
/// ## Errors
///
/// This might return `WlExtensionNotSupported` if binding is not supported
/// by the EGL implementation.
///
/// This might return `OtherEGLDisplayAlreadyBound` if called for the same
/// `Display` multiple times, as only one context may be bound at any given time.
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError>;
// unsafe fn egl_image_to_texture(&self, image: ffi::egl::types::EGLImage, tex_id: c_uint) -> ::std::result::Result<(), EglExtensionNotSupportedError>;
}

View File

@ -0,0 +1,254 @@
use super::ffi;
use super::error::*;
#[cfg(feature = "backend_drm")]
use ::backend::drm::error::{Error as DrmError, ErrorKind as DrmErrorKind, Result as DrmResult};
#[cfg(feature = "backend_drm")]
use gbm::{AsRaw, Device as GbmDevice, Format as GbmFormat, BufferObjectFlags, Surface as GbmSurface};
#[cfg(feature = "backend_drm")]
use std::marker::PhantomData;
#[cfg(any(feature = "backend_drm", feature = "backend_winit"))]
use std::ptr;
#[cfg(feature = "backend_drm")]
use std::os::unix::io::AsRawFd;
#[cfg(feature = "backend_winit")]
use winit::Window as WinitWindow;
#[cfg(feature = "backend_winit")]
use winit::os::unix::WindowExt;
#[cfg(feature = "backend_winit")]
use nix::libc::c_void;
#[cfg(feature = "backend_winit")]
use wayland_client::egl as wegl;
pub trait Backend {
type Surface: NativeSurface;
unsafe fn get_display<F: Fn(&str) -> bool>(
display: ffi::NativeDisplayType,
has_dp_extension: F,
log: ::slog::Logger,
) -> ffi::egl::types::EGLDisplay;
}
#[cfg(feature = "backend_winit")]
pub enum Wayland {}
#[cfg(feature = "backend_winit")]
impl Backend for Wayland {
type Surface = wegl::WlEglSurface;
unsafe fn get_display<F>(display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger)
-> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool
{
if has_dp_extension("EGL_KHR_platform_wayland")
&& ffi::egl::GetPlatformDisplay::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
ffi::egl::GetPlatformDisplay(
ffi::egl::PLATFORM_WAYLAND_KHR,
display as *mut _,
ptr::null(),
)
} else if has_dp_extension("EGL_EXT_platform_wayland")
&& ffi::egl::GetPlatformDisplayEXT::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_EXT_platform_wayland");
ffi::egl::GetPlatformDisplayEXT(
ffi::egl::PLATFORM_WAYLAND_EXT,
display as *mut _,
ptr::null(),
)
} else {
trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _)
}
}
}
pub struct XlibWindow(*const c_void);
#[cfg(feature = "backend_winit")]
pub enum X11 {}
#[cfg(feature = "backend_winit")]
impl Backend for X11 {
type Surface = XlibWindow;
unsafe fn get_display<F>(display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger)
-> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool
{
if has_dp_extension("EGL_KHR_platform_x11")
&& ffi::egl::GetPlatformDisplay::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
} else if has_dp_extension("EGL_EXT_platform_x11")
&& ffi::egl::GetPlatformDisplayEXT::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
} else {
trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _)
}
}
}
#[cfg(feature = "backend_drm")]
pub struct Gbm<T: 'static> {
_userdata: PhantomData<T>,
}
#[cfg(feature = "backend_drm")]
impl<T: 'static> Backend for Gbm<T> {
type Surface = GbmSurface<T>;
unsafe fn get_display<F>(display: ffi::NativeDisplayType, has_dp_extension: F, log: ::slog::Logger)
-> ffi::egl::types::EGLDisplay
where
F: Fn(&str) -> bool
{
if has_dp_extension("EGL_KHR_platform_gbm")
&& ffi::egl::GetPlatformDisplay::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
} else if has_dp_extension("EGL_MESA_platform_gbm")
&& ffi::egl::GetPlatformDisplayEXT::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
} else if has_dp_extension("EGL_MESA_platform_gbm")
&& ffi::egl::GetPlatformDisplay::is_loaded()
{
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
} else {
trace!(log, "Default EGL Display Initialization via GetDisplay");
ffi::egl::GetDisplay(display as *mut _)
}
}
}
/// Trait for types returning Surfaces which can be used to initialize `EGLSurface`s
///
/// ## Unsafety
///
/// The returned `NativeDisplayType` must be valid for egl and there is no way to test that.
pub unsafe trait NativeDisplay<B: Backend> {
type Arguments;
type Error: ::std::error::Error + Send + 'static;
/// Because one typ might implement multiple `Backend` this function must be called to check
/// if the expected `Backend` is used at runtime.
fn is_backend(&self) -> bool;
/// Return a raw pointer egl will accept for context creation.
fn ptr(&self) -> Result<ffi::NativeDisplayType>;
/// Create a surface
fn create_surface(&self, args: Self::Arguments) -> ::std::result::Result<B::Surface, Self::Error>;
}
#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<X11> for WinitWindow {
type Arguments = ();
type Error = Error;
fn is_backend(&self) -> bool {
self.get_xlib_display().is_some()
}
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
self.get_xlib_display()
.map(|ptr| ptr as *const _)
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
}
fn create_surface(&self, _args: ()) -> Result<XlibWindow> {
self.get_xlib_window()
.map(|ptr| XlibWindow(ptr))
.ok_or(ErrorKind::NonMatchingBackend("X11").into())
}
}
#[cfg(feature = "backend_winit")]
unsafe impl NativeDisplay<Wayland> for WinitWindow {
type Arguments = ();
type Error = Error;
fn is_backend(&self) -> bool {
self.get_wayland_display().is_some()
}
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
self.get_wayland_display()
.map(|ptr| ptr as *const _)
.ok_or(ErrorKind::NonMatchingBackend("Wayland").into())
}
fn create_surface(&self, _args: ()) -> Result<wegl::WlEglSurface> {
if let Some(surface) = self.get_wayland_surface() {
let (w, h) = self.get_inner_size().unwrap();
Ok(unsafe { wegl::WlEglSurface::new_from_raw(surface as *mut _, w as i32, h as i32) })
} else {
bail!(ErrorKind::NonMatchingBackend("Wayland"))
}
}
}
#[cfg(feature = "backend_drm")]
/// Arguments necessary to construct a `GbmSurface`
pub struct GbmSurfaceArguments {
/// Size of the surface
pub size: (u32, u32),
/// Pixel format of the surface
pub format: GbmFormat,
/// Flags for surface creation
pub flags: BufferObjectFlags,
}
#[cfg(feature = "backend_drm")]
unsafe impl<A: AsRawFd + 'static, T: 'static> NativeDisplay<Gbm<T>> for GbmDevice<A> {
type Arguments = GbmSurfaceArguments;
type Error = DrmError;
fn is_backend(&self) -> bool { true }
fn ptr(&self) -> Result<ffi::NativeDisplayType> {
Ok(self.as_raw() as *const _)
}
fn create_surface(&self, args: GbmSurfaceArguments) -> DrmResult<GbmSurface<T>> {
use backend::drm::error::ResultExt as DrmResultExt;
DrmResultExt::chain_err(GbmDevice::create_surface(
self,
args.size.0,
args.size.1,
args.format,
args.flags,
), || DrmErrorKind::GbmInitFailed)
}
}
/// Trait for types returning valid surface pointers for initializing egl
///
/// ## Unsafety
///
/// The returned `NativeWindowType` must be valid for egl and there is no way to test that.
pub unsafe trait NativeSurface {
/// Return a raw pointer egl will accept for surface creation.
fn ptr(&self) -> ffi::NativeWindowType;
}
#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for XlibWindow {
fn ptr(&self) -> ffi::NativeWindowType { self.0 }
}
#[cfg(feature = "backend_winit")]
unsafe impl NativeSurface for wegl::WlEglSurface {
fn ptr(&self) -> ffi::NativeWindowType { self.ptr() as *const _ }
}
#[cfg(feature = "backend_drm")]
unsafe impl<T: 'static> NativeSurface for GbmSurface<T> {
fn ptr(&self) -> ffi::NativeWindowType { self.as_raw() as *const _ }
}

View File

@ -0,0 +1,125 @@
use super::{EGLContext, SwapBuffersError};
use super::error::*;
use super::ffi;
use super::native;
use std::ops::{Deref, DerefMut};
use std::rc::{Rc, Weak};
/// EGL surface of a given egl context for rendering
pub struct EGLSurface<N: native::NativeSurface> {
context: Weak<ffi::egl::types::EGLContext>,
display: Weak<ffi::egl::types::EGLDisplay>,
native: N,
surface: ffi::egl::types::EGLSurface,
}
impl<N: native::NativeSurface> Deref for EGLSurface<N> {
type Target = N;
fn deref(&self) -> &N {
&self.native
}
}
impl<N: native::NativeSurface> DerefMut for EGLSurface<N> {
fn deref_mut(&mut self) -> &mut N {
&mut self.native
}
}
impl<N: native::NativeSurface> EGLSurface<N> {
pub(crate) fn new<B: native::Backend<Surface=N>, D: native::NativeDisplay<B>>(
context: &EGLContext<B, D>,
native: N,
) -> Result<EGLSurface<N>> {
let surface = unsafe {
ffi::egl::CreateWindowSurface(
*context.display,
context.config_id,
native.ptr(),
context.surface_attributes.as_ptr(),
)
};
if surface.is_null() {
bail!(ErrorKind::SurfaceCreationFailed);
}
Ok(EGLSurface {
context: Rc::downgrade(&context.context),
display: Rc::downgrade(&context.display),
native,
surface,
})
}
/// Swaps buffers at the end of a frame.
pub fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
if let Some(display) = self.display.upgrade() {
let ret = unsafe {
ffi::egl::SwapBuffers((*display) as *const _, self.surface as *const _)
};
if ret == 0 {
match unsafe { ffi::egl::GetError() } as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => Err(SwapBuffersError::Unknown(err)),
}
} else {
Ok(())
}
} else {
Err(SwapBuffersError::ContextLost)
}
}
/// Makes the OpenGL context the current context in the current thread.
///
/// # Unsafety
///
/// This function is marked unsafe, because the context cannot be made current
/// on multiple threads.
pub unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
if let (Some(display), Some(context)) = (self.display.upgrade(), self.context.upgrade()) {
let ret = ffi::egl::MakeCurrent(
(*display) as *const _,
self.surface as *const _,
self.surface as *const _,
(*context) as *const _,
);
if ret == 0 {
match ffi::egl::GetError() as u32 {
ffi::egl::CONTEXT_LOST => Err(SwapBuffersError::ContextLost),
err => panic!("eglMakeCurrent failed (eglGetError returned 0x{:x})", err),
}
} else {
Ok(())
}
} else {
Err(SwapBuffersError::ContextLost)
}
}
/// Returns true if the OpenGL surface is the current one in the thread.
pub fn is_current(&self) -> bool {
if self.context.upgrade().is_some() {
unsafe {
ffi::egl::GetCurrentSurface(ffi::egl::DRAW as _) == self.surface as *const _
&& ffi::egl::GetCurrentSurface(ffi::egl::READ as _) == self.surface as *const _
}
} else {
false
}
}
}
impl<N: native::NativeSurface> Drop for EGLSurface<N> {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
unsafe {
ffi::egl::DestroySurface((*display) as *const _, self.surface as *const _);
}
}
}
}

View File

@ -9,6 +9,8 @@
//! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this //! See also `examples/udev.rs` for pure hardware backed example of a compositor utilizing this
//! backend. //! backend.
use drm::Device as BasicDevice;
use drm::control::Device as ControlDevice;
use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler}; use backend::drm::{drm_device_bind, DrmBackend, DrmDevice, DrmHandler};
use backend::session::{Session, SessionObserver}; use backend::session::{Session, SessionObserver};
use nix::fcntl; use nix::fcntl;
@ -18,28 +20,38 @@ use std::collections::HashMap;
use std::ffi::OsString; use std::ffi::OsString;
use std::io::{Error as IoError, Result as IoResult}; use std::io::{Error as IoError, Result as IoResult};
use std::mem::drop; use std::mem::drop;
use std::os::unix::io::AsRawFd; use std::os::unix::io::{AsRawFd, RawFd};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult}; use udev::{Context, Enumerator, Event, EventType, MonitorBuilder, MonitorSocket, Result as UdevResult};
use wayland_server::{EventLoopHandle, StateProxy, StateToken}; use wayland_server::{EventLoopHandle, StateProxy, StateToken};
use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest}; use wayland_server::sources::{FdEventSource, FdEventSourceImpl, FdInterest};
pub struct SessionFdDrmDevice(RawFd);
impl AsRawFd for SessionFdDrmDevice {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
impl BasicDevice for SessionFdDrmDevice {}
impl ControlDevice for SessionFdDrmDevice {}
/// Graphical backend that monitors available drm devices. /// Graphical backend that monitors available drm devices.
/// ///
/// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the /// Provides a way to automatically initialize a `DrmDevice` for available gpus and notifies the
/// given handler of any changes. Can be used to provide hot-plug functionality for gpus and /// given handler of any changes. Can be used to provide hot-plug functionality for gpus and
/// attached monitors. /// attached monitors.
pub struct UdevBackend< pub struct UdevBackend<
B: Borrow<DrmBackend> + 'static, B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static,
S: Session + 'static, S: Session + 'static,
T: UdevHandler<B, H> + 'static, T: UdevHandler<B, H> + 'static,
> { > {
devices: HashMap< devices: HashMap<
dev_t, dev_t,
( (
StateToken<DrmDevice<B>>, StateToken<DrmDevice<SessionFdDrmDevice, B>>,
FdEventSource<(StateToken<DrmDevice<B>>, H)>, FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice, B>>, H)>,
), ),
>, >,
monitor: MonitorSocket, monitor: MonitorSocket,
@ -49,8 +61,8 @@ pub struct UdevBackend<
} }
impl< impl<
B: From<DrmBackend> + Borrow<DrmBackend> + 'static, B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static,
S: Session + 'static, S: Session + 'static,
T: UdevHandler<B, H> + 'static, T: UdevHandler<B, H> + 'static,
> UdevBackend<B, H, S, T> { > UdevBackend<B, H, S, T> {
@ -75,10 +87,10 @@ impl<
.into_iter() .into_iter()
// Create devices // Create devices
.flat_map(|path| { .flat_map(|path| {
match unsafe { DrmDevice::new_from_fd( match unsafe { DrmDevice::new(
{ {
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) { match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
Ok(fd) => fd, Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => { Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
return None; return None;
@ -134,7 +146,7 @@ impl<
} }
} }
}) })
.collect::<HashMap<dev_t, (StateToken<DrmDevice<B>>, FdEventSource<(StateToken<DrmDevice<B>>, H)>)>>(); .collect::<HashMap<dev_t, (StateToken<DrmDevice<SessionFdDrmDevice, B>>, FdEventSource<(StateToken<DrmDevice<SessionFdDrmDevice, B>>, H)>)>>();
let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?; let mut builder = MonitorBuilder::new(context).chain_err(|| ErrorKind::FailedToInitMonitor)?;
builder builder
@ -181,8 +193,8 @@ impl<
} }
impl< impl<
B: Borrow<DrmBackend> + 'static, B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static,
S: Session + 'static, S: Session + 'static,
T: UdevHandler<B, H> + 'static, T: UdevHandler<B, H> + 'static,
> SessionObserver for StateToken<UdevBackend<B, H, S, T>> { > SessionObserver for StateToken<UdevBackend<B, H, S, T>> {
@ -211,8 +223,8 @@ pub fn udev_backend_bind<B, S, H, T>(
evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>> evlh: &mut EventLoopHandle, udev: StateToken<UdevBackend<B, H, S, T>>
) -> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>> ) -> IoResult<FdEventSource<StateToken<UdevBackend<B, H, S, T>>>>
where where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static, B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static,
T: UdevHandler<B, H> + 'static, T: UdevHandler<B, H> + 'static,
S: Session + 'static, S: Session + 'static,
{ {
@ -222,8 +234,8 @@ where
fn fd_event_source_implementation<B, S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>> fn fd_event_source_implementation<B, S, H, T>() -> FdEventSourceImpl<StateToken<UdevBackend<B, H, S, T>>>
where where
B: From<DrmBackend> + Borrow<DrmBackend> + 'static, B: From<DrmBackend<SessionFdDrmDevice>> + Borrow<DrmBackend<SessionFdDrmDevice>> + 'static,
H: DrmHandler<B> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static,
T: UdevHandler<B, H> + 'static, T: UdevHandler<B, H> + 'static,
S: Session + 'static, S: Session + 'static,
{ {
@ -242,7 +254,7 @@ where
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) { if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
let mut device = { let mut device = {
match unsafe { match unsafe {
DrmDevice::new_from_fd( DrmDevice::new(
{ {
let logger = evlh.state().get(token).logger.clone(); let logger = evlh.state().get(token).logger.clone();
match evlh.state().get_mut(token).session.open( match evlh.state().get_mut(token).session.open(
@ -250,7 +262,7 @@ where
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
| fcntl::O_NONBLOCK, | fcntl::O_NONBLOCK,
) { ) {
Ok(fd) => fd, Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => { Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err); warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
continue; continue;
@ -365,7 +377,7 @@ where
} }
/// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime. /// Handler for the `UdevBackend`, allows to open, close and update drm devices as they change during runtime.
pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'static> pub trait UdevHandler<B: Borrow<DrmBackend<SessionFdDrmDevice>> + 'static, H: DrmHandler<SessionFdDrmDevice, B> + 'static>
{ {
/// Called on initialization for every known device and when a new device is detected. /// Called on initialization for every known device and when a new device is detected.
/// ///
@ -373,7 +385,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stati
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<B>) fn device_added<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &mut DrmDevice<SessionFdDrmDevice, B>)
-> Option<H>; -> Option<H>;
/// Called when an open device is changed. /// Called when an open device is changed.
/// ///
@ -382,7 +394,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stati
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>); fn device_changed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice, B>>);
/// Called when a device was removed. /// Called when a device was removed.
/// ///
/// The device will not accept any operations anymore and its file descriptor will be closed once /// The device will not accept any operations anymore and its file descriptor will be closed once
@ -390,7 +402,7 @@ pub trait UdevHandler<B: Borrow<DrmBackend> + 'static, H: DrmHandler<B> + 'stati
/// ///
/// ## Panics /// ## Panics
/// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`. /// Panics if you try to borrow the token of the belonging `UdevBackend` using this `StateProxy`.
fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<B>>); fn device_removed<'a, S: Into<StateProxy<'a>>>(&mut self, state: S, device: &StateToken<DrmDevice<SessionFdDrmDevice, B>>);
/// Called when the udev context has encountered and error. /// Called when the udev context has encountered and error.
/// ///
/// ## Panics /// ## Panics

View File

@ -1,44 +1,23 @@
//! Implementation of backend traits for types provided by `winit` //! Implementation of backend traits for types provided by `winit`
use backend::graphics::GraphicsBackend; use backend::graphics::GraphicsBackend;
use backend::graphics::egl::{self, EGLContext, EGLGraphicsBackend, GlAttributes, PixelFormat, use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError};
PixelFormatRequirements, SwapBuffersError}; use backend::graphics::egl::error as egl_error;
use backend::graphics::egl::native;
use backend::graphics::egl::context::{GlAttributes, PixelFormatRequirements};
use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState, use backend::input::{Axis, AxisSource, Event as BackendEvent, InputBackend, InputHandler, KeyState,
KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent, KeyboardKeyEvent, MouseButton, MouseButtonState, PointerAxisEvent, PointerButtonEvent,
PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent, PointerMotionAbsoluteEvent, Seat, SeatCapabilities, TouchCancelEvent, TouchDownEvent,
TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent}; TouchMotionEvent, TouchSlot, TouchUpEvent, UnusedEvent};
use nix::libc::c_void; use nix::libc::c_void;
use rental::TryNewError;
use std::cell::Cell;
use std::cmp; use std::cmp;
use std::error; use std::error;
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor, use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as WinitMouseButton, MouseCursor,
MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent}; MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent, Window as WinitWindow};
use winit::os::unix::WindowExt; use wayland_client::egl as wegl;
use wayland_server::Display;
rental! {
mod rental {
use std::boxed::Box;
use ::winit::{Window as WinitWindow};
use ::backend::graphics::egl::{EGLContext, EGLSurface};
#[rental]
pub struct Window {
window: Box<WinitWindow>,
egl: EGL<'window>,
}
#[rental]
pub struct EGL<'a> {
context: Box<EGLContext<'a, WinitWindow>>,
surface: EGLSurface<'context, 'a, WinitWindow>,
}
}
}
use self::rental::{Window, EGL};
error_chain! { error_chain! {
errors { errors {
@ -46,23 +25,42 @@ error_chain! {
InitFailed { InitFailed {
description("Failed to initialize a window") description("Failed to initialize a window")
} }
#[doc = "Context creation is not supported on the current window system"]
NotSupported {
description("Context creation is not supported on the current window system.")
}
} }
links { links {
EGL(egl::Error, egl::ErrorKind) #[doc = "EGL error"]; EGL(egl_error::Error, egl_error::ErrorKind) #[doc = "EGL error"];
} }
} }
impl<H> From<TryNewError<Error, H>> for Error { enum Window {
fn from(err: TryNewError<Error, H>) -> Error { Wayland {
err.0 context: EGLContext<native::Wayland, WinitWindow>,
surface: EGLSurface<wegl::WlEglSurface>,
},
X11 {
context: EGLContext<native::X11, WinitWindow>,
surface: EGLSurface<native::XlibWindow>,
},
}
impl Window {
fn window(&self) -> &WinitWindow {
match self {
&Window::Wayland { ref context, .. } => &**context,
&Window::X11 { ref context, .. } => &**context,
} }
} }
}
/// Window with an active EGL Context created by `winit`. Implements the /// Window with an active EGL Context created by `winit`. Implements the
/// `EGLGraphicsBackend` graphics backend trait /// `EGLGraphicsBackend` graphics backend trait
pub struct WinitGraphicsBackend { pub struct WinitGraphicsBackend {
window: Rc<Window>, window: Rc<Window>,
ready: Cell<bool>,
logger: ::slog::Logger, logger: ::slog::Logger,
} }
@ -136,31 +134,46 @@ where
.chain_err(|| ErrorKind::InitFailed)?; .chain_err(|| ErrorKind::InitFailed)?;
debug!(log, "Window created"); debug!(log, "Window created");
let window = Rc::new(Window::try_new(Box::new(winit_window), |window| { let reqs = PixelFormatRequirements {
EGL::try_new(
Box::new(match EGLContext::new_from_winit(
&*window,
attributes,
PixelFormatRequirements {
hardware_accelerated: Some(true), hardware_accelerated: Some(true),
color_bits: Some(24), color_bits: Some(24),
alpha_bits: Some(8), alpha_bits: Some(8),
..Default::default() ..Default::default()
}, };
let window = Rc::new(
if native::NativeDisplay::<native::Wayland>::is_backend(&winit_window) {
let context = EGLContext::<native::Wayland, WinitWindow>::new(
winit_window,
attributes,
reqs,
log.clone(), log.clone(),
) { )?;
Ok(context) => context, let surface = context.create_surface(())?;
Err(err) => bail!(err), Window::Wayland {
}), context,
|context| context.create_surface(window), surface
).map_err(egl::Error::from) }
.map_err(Error::from) } else if native::NativeDisplay::<native::X11>::is_backend(&winit_window) {
})?); let context = EGLContext::<native::X11, WinitWindow>::new(
winit_window,
attributes,
reqs,
log.clone(),
)?;
let surface = context.create_surface(())?;
Window::X11 {
context,
surface
}
} else {
bail!(ErrorKind::NotSupported);
}
);
Ok(( Ok((
WinitGraphicsBackend { WinitGraphicsBackend {
window: window.clone(), window: window.clone(),
ready: Cell::new(false),
logger: log.new(o!("smithay_winit_component" => "graphics")), logger: log.new(o!("smithay_winit_component" => "graphics")),
}, },
WinitInputBackend { WinitInputBackend {
@ -189,7 +202,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> { fn set_cursor_position(&self, x: u32, y: u32) -> ::std::result::Result<(), ()> {
debug!(self.logger, "Setting cursor position to {:?}", (x, y)); debug!(self.logger, "Setting cursor position to {:?}", (x, y));
self.window.head().set_cursor_position(x as i32, y as i32) self.window.window().set_cursor_position(x as i32, y as i32)
} }
fn set_cursor_representation( fn set_cursor_representation(
@ -197,7 +210,7 @@ impl GraphicsBackend for WinitGraphicsBackend {
) -> ::std::result::Result<(), ()> { ) -> ::std::result::Result<(), ()> {
// Cannot log this one, as `CursorFormat` is not `Debug` and should not be // Cannot log this one, as `CursorFormat` is not `Debug` and should not be
debug!(self.logger, "Changing cursor representation"); debug!(self.logger, "Changing cursor representation");
self.window.head().set_cursor(*cursor); self.window.window().set_cursor(*cursor);
Ok(()) Ok(())
} }
} }
@ -205,47 +218,73 @@ impl GraphicsBackend for WinitGraphicsBackend {
impl EGLGraphicsBackend for WinitGraphicsBackend { impl EGLGraphicsBackend for WinitGraphicsBackend {
fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> { fn swap_buffers(&self) -> ::std::result::Result<(), SwapBuffersError> {
trace!(self.logger, "Swapping buffers"); trace!(self.logger, "Swapping buffers");
if !self.ready.get() { match *self.window {
if self.window.head().is_ready() { Window::Wayland { ref surface, .. } => surface.swap_buffers(),
// avoid locking the mutex every time once the window is ready Window::X11 { ref surface, .. } => surface.swap_buffers(),
self.ready.set(true);
} else {
// Not yet ready, just silently ignore the swap-buffers call
return Ok(());
} }
} }
self.window
.rent(|egl| egl.rent(|surface| surface.swap_buffers()))
}
unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void { unsafe fn get_proc_address(&self, symbol: &str) -> *const c_void {
trace!(self.logger, "Getting symbol for {:?}", symbol); trace!(self.logger, "Getting symbol for {:?}", symbol);
self.window.rent(|egl| egl.head().get_proc_address(symbol)) match *self.window {
Window::Wayland { ref context, .. } => context.get_proc_address(symbol),
Window::X11 { ref context, .. } => context.get_proc_address(symbol),
}
} }
fn get_framebuffer_dimensions(&self) -> (u32, u32) { fn get_framebuffer_dimensions(&self) -> (u32, u32) {
self.window self.window
.head() .window()
.get_inner_size_pixels() .get_inner_size()
.expect("Window does not exist anymore") .expect("Window does not exist anymore")
} }
fn is_current(&self) -> bool { fn is_current(&self) -> bool {
self.window match *self.window {
.rent(|egl| egl.rent_all(|egl| egl.context.is_current() && egl.surface.is_current())) Window::Wayland { ref context, ref surface } => context.is_current() && surface.is_current(),
Window::X11 { ref context, ref surface } => context.is_current() && surface.is_current(),
}
} }
unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> { unsafe fn make_current(&self) -> ::std::result::Result<(), SwapBuffersError> {
debug!(self.logger, "Setting EGL context to be the current context"); trace!(self.logger, "Setting EGL context to be the current context");
self.window match *self.window {
.rent(|egl| egl.rent(|surface| surface.make_current())) Window::Wayland { ref surface, .. } => surface.make_current(),
Window::X11 { ref surface, .. } => surface.make_current(),
}
} }
fn get_pixel_format(&self) -> PixelFormat { fn get_pixel_format(&self) -> PixelFormat {
self.window.rent(|egl| egl.head().get_pixel_format()) match *self.window {
Window::Wayland { ref context, .. } => context.get_pixel_format(),
Window::X11 { ref context, .. } => context.get_pixel_format(),
} }
} }
fn bind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
match *self.window {
Window::Wayland { ref context, .. } => context.bind_wl_display(display),
Window::X11 { ref context, .. } => context.bind_wl_display(display),
}?;
Ok(())
}
fn unbind_wl_display(&self, display: &Display) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
match *self.window {
Window::Wayland { ref context, .. } => context.unbind_wl_display(display),
Window::X11 { ref context, .. } => context.unbind_wl_display(display),
}?;
Ok(())
}
/*
unsafe fn egl_image_to_texture(&self, image: EGLImage, tex_id: c_uint) -> ::std::result::Result<(), EglExtensionNotSupportedError> {
self.window.rent(|egl| egl.head().egl_image_to_texture(image, tex_id))?;
Ok(())
}
*/
}
/// Errors that may happen when driving the event loop of `WinitInputBackend` /// Errors that may happen when driving the event loop of `WinitInputBackend`
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum WinitInputError { pub enum WinitInputError {
@ -326,8 +365,8 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
cmp::min( cmp::min(
(self.x * width as f64 (self.x * width as f64
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((width, 0)) .unwrap_or((width, 0))
.0 as f64) as u32, .0 as f64) as u32,
0, 0,
@ -338,8 +377,8 @@ impl PointerMotionAbsoluteEvent for WinitMouseMovedEvent {
cmp::min( cmp::min(
(self.y * height as f64 (self.y * height as f64
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((0, height)) .unwrap_or((0, height))
.1 as f64) as u32, .1 as f64) as u32,
0, 0,
@ -439,8 +478,8 @@ impl TouchDownEvent for WinitTouchStartedEvent {
cmp::min( cmp::min(
self.location.0 as i32 * width as i32 self.location.0 as i32 * width as i32
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((width, 0)) .unwrap_or((width, 0))
.0 as i32, .0 as i32,
0, 0,
@ -451,8 +490,8 @@ impl TouchDownEvent for WinitTouchStartedEvent {
cmp::min( cmp::min(
self.location.1 as i32 * height as i32 self.location.1 as i32 * height as i32
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((0, height)) .unwrap_or((0, height))
.1 as i32, .1 as i32,
0, 0,
@ -491,8 +530,8 @@ impl TouchMotionEvent for WinitTouchMovedEvent {
fn x_transformed(&self, width: u32) -> u32 { fn x_transformed(&self, width: u32) -> u32 {
self.location.0 as u32 * width self.location.0 as u32 * width
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((width, 0)) .unwrap_or((width, 0))
.0 .0
} }
@ -500,8 +539,8 @@ impl TouchMotionEvent for WinitTouchMovedEvent {
fn y_transformed(&self, height: u32) -> u32 { fn y_transformed(&self, height: u32) -> u32 {
self.location.1 as u32 * height self.location.1 as u32 * height
/ self.window / self.window
.head() .window()
.get_inner_size_points() .get_inner_size()
.unwrap_or((0, height)) .unwrap_or((0, height))
.1 .1
} }
@ -626,14 +665,11 @@ impl InputBackend for WinitInputBackend {
match (event, handler.as_mut()) { match (event, handler.as_mut()) {
(WindowEvent::Resized(x, y), _) => { (WindowEvent::Resized(x, y), _) => {
trace!(logger, "Resizing window to {:?}", (x, y)); trace!(logger, "Resizing window to {:?}", (x, y));
window.head().set_inner_size(x, y); window.window().set_inner_size(x, y);
window.rent(|egl| { match **window {
egl.rent(|surface| { Window::Wayland { ref surface, .. } => surface.resize(x as i32, y as i32, 0, 0),
if let Some(wegl_surface) = (**surface).as_ref() { _ => {},
wegl_surface.resize(x as i32, y as i32, 0, 0) };
}
})
});
} }
( (
WindowEvent::KeyboardInput { WindowEvent::KeyboardInput {
@ -667,7 +703,7 @@ impl InputBackend for WinitInputBackend {
) )
} }
( (
WindowEvent::MouseMoved { WindowEvent::CursorMoved {
position: (x, y), .. position: (x, y), ..
}, },
Some(handler), Some(handler),

View File

@ -8,10 +8,8 @@
#![recursion_limit = "1024"] #![recursion_limit = "1024"]
extern crate image; extern crate image;
#[macro_use] #[cfg_attr(feature = "backend_session", macro_use)]
extern crate nix; extern crate nix;
#[macro_use]
extern crate rental;
extern crate tempfile; extern crate tempfile;
extern crate wayland_protocols; extern crate wayland_protocols;
extern crate wayland_server; extern crate wayland_server;

View File

@ -1,8 +1,8 @@
use ::backend::graphics::egl::ffi; use ::backend::graphics::egl::ffi;
use ::backend::graphics::egl::EGLContext; use ::backend::graphics::egl::{EGLContext, NativeSurface};
pub use ::backend::graphics::egl::ffi::EGLImage; use ::backend::graphics::egl::EGLImage;
use nix::libc::c_int;
use wayland_server::protocol::wl_buffer::WlBuffer; use wayland_server::protocol::wl_buffer::WlBuffer;
use wayland_server::Resource;
/// Error that can occur when accessing an EGL buffer /// Error that can occur when accessing an EGL buffer
#[derive(Debug)] #[derive(Debug)]
@ -13,14 +13,14 @@ pub enum BufferAccessError {
FailedToCreateEGLImage, FailedToCreateEGLImage,
} }
#[repr(u32)] #[repr(i32)]
pub enum Format { pub enum Format {
RGB = ffi::TEXTURE_RGB, RGB = ffi::egl::TEXTURE_RGB as i32,
RGBA = ffi::TEXTURE_RGBA, RGBA = ffi::egl::TEXTURE_RGBA as i32,
External = ffi::TEXTURE_EXTERNAL_WL, External = ffi::egl::TEXTURE_EXTERNAL_WL,
Y_UV = ffi::TEXTURE_Y_UV_WL, Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
Y_U_V = ffi::TEXTURE_Y_U_V_WL, Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
Y_XUXV = ffi::TEXTURE_Y_XUXV_WL, Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
} }
impl Format { impl Format {
@ -33,46 +33,40 @@ impl Format {
} }
} }
pub struct Attributes { pub struct EGLImages {
width: u32, pub width: u32,
height: u32, pub height: u32,
y_inverted: bool, pub y_inverted: bool,
format: Format, pub format: Format,
images: Vec<EGLImage>,
buffer: WlBuffer,
} }
pub fn with_buffer_contents<F>(buffer: &WlBuffer, context: &EGLContext, f: F) -> Result<(), BufferAccessError> pub fn buffer_contents<T: NativeSurface>(buffer: WlBuffer, context: &EGLContext<T>) -> Result<(Vec<EGLImages>, attributes: Attributes), BufferAccessError>
where where
F: FnOnce(Attributes, Vec<EGLImage>)
{ {
let mut format: u32 = 0; let mut format: i32 = 0;
if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::TEXTURE_FORMAT, &mut format as *mut _) == 0 { if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } {
return Err(BufferAccessError::NotManaged); return Err(BufferAccessError::NotManaged);
} }
let mut width: u32 = 0; let mut width: i32 = 0;
if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::WIDTH, &mut width as *mut _) == 0 { if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } {
return Err(BufferAccessError::NotManaged); return Err(BufferAccessError::NotManaged);
} }
let mut height: u32 = 0; let mut height: i32 = 0;
if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::HEIGHT, &mut height as *mut _) == 0 { if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } {
return Err(BufferAccessError::NotManaged); return Err(BufferAccessError::NotManaged);
} }
let mut inverted: u32 = 0; let mut inverted: i32 = 0;
if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) == 0 { if unsafe { ffi::egl::QueryWaylandBufferWL(context.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) == 0 } {
inverted = 1; inverted = 1;
} }
let attributes = Attributes { let mut images = Vec::with_capacity(attributes.format.num_planes() as usize);
width, for i in 0..attributes.format.num_planes() {
height,
y_inverted = inverted != 0,
format: format as Format,
};
let mut images = Vec::with_capacity(attributes.format.num_planes());
for _ in 0..attributes.format.num_planes() {
let mut out = Vec::with_capacity(3); let mut out = Vec::with_capacity(3);
out.push(ffi::egl::WAYLAND_PLANE_WL as i32); out.push(ffi::egl::WAYLAND_PLANE_WL as i32);
out.push(i as i32); out.push(i as i32);
@ -80,13 +74,13 @@ where
images.push({ images.push({
let image = let image =
ffi::egl::CreateImageKHR( unsafe { ffi::egl::CreateImageKHR(
context.display, context.display,
ffi::egl::NO_CONTEXT, ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL, ffi::egl::WAYLAND_BUFFER_WL,
buffer.ptr(), buffer.ptr() as *mut _,
out.as_ptr(), out.as_ptr(),
); ) };
if image == ffi::egl::NO_IMAGE_KHR { if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::FailedToCreateEGLImage); return Err(BufferAccessError::FailedToCreateEGLImage);
} else { } else {
@ -95,5 +89,22 @@ where
}); });
} }
f(attributes, images) let result = EGLImages {
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format: match format {
x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB,
x if x == ffi::egl::TEXTURE_RGBA as i32 => Format::RGBA,
ffi::egl::TEXTURE_EXTERNAL_WL => Format::External,
ffi::egl::TEXTURE_Y_UV_WL => Format::Y_UV,
ffi::egl::TEXTURE_Y_U_V_WL => Format::Y_U_V,
ffi::egl::TEXTURE_Y_XUXV_WL => Format::Y_XUXV,
_ => panic!("EGL returned invalid texture type"),
},
images,
buffer,
};
Ok(result)
} }

View File

@ -19,6 +19,7 @@
//! quickly encounter a panic. //! quickly encounter a panic.
pub mod compositor; pub mod compositor;
//pub mod drm;
pub mod output; pub mod output;
pub mod seat; pub mod seat;
pub mod shm; pub mod shm;