From 6c6d54064d991b9b34ca077f600bda6e835e8532 Mon Sep 17 00:00:00 2001 From: Drakulix Date: Thu, 28 Dec 2017 15:28:15 +0100 Subject: [PATCH] Get it to work... --- examples/helpers/glium.rs | 66 +++++++++---- examples/helpers/implementations.rs | 116 +++++++++++----------- examples/winit.rs | 22 ++--- src/backend/graphics/egl/context.rs | 59 +++++++++-- src/backend/graphics/egl/error.rs | 10 -- src/backend/graphics/egl/mod.rs | 5 +- src/backend/graphics/egl/wayland.rs | 145 +++++++++++++++++++++++++--- src/backend/winit.rs | 21 ++-- src/wayland/compositor/mod.rs | 9 +- 9 files changed, 309 insertions(+), 144 deletions(-) diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs index d8a53ba..ac17e7c 100644 --- a/examples/helpers/glium.rs +++ b/examples/helpers/glium.rs @@ -3,7 +3,8 @@ use glium::{Frame, Surface, GlObject}; use glium::backend::Facade; use glium::index::PrimitiveType; use glium::texture::{MipmapsOption, UncompressedFloatFormat, Texture2d}; -use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImage}; +use smithay::backend::graphics::egl::EGLGraphicsBackend; +use smithay::backend::graphics::egl::wayland::{Format, EGLImages}; use smithay::backend::graphics::glium::GliumGraphicsBackend; use std::borrow::Borrow; use std::ops::Deref; @@ -108,45 +109,52 @@ impl> + EGLGraphicsBackend + 'static> From fo } impl GliumDrawer { - pub fn texture_from_mem(&self, contents: &[u8], surface_dimensions: (u32, u32)) { + pub fn texture_from_mem(&self, contents: &[u8], surface_dimensions: (u32, u32)) -> Texture2d { let image = glium::texture::RawImage2d { data: contents.into(), width: surface_dimensions.0, height: surface_dimensions.1, format: glium::texture::ClientFormat::U8U8U8U8, }; - let opengl_texture = Texture2d::new(&self.display, image).unwrap(); + Texture2d::new(&self.display, image).unwrap() } - pub fn texture_from_egl(&self, image: EGLImage, format: UncompressedFloatFormat, - surface_dimensions: (u32, u32)) - -> Texture2d + pub fn texture_from_egl(&self, images: &EGLImages) + -> Option { + let format = match images.format { + Format::RGB => UncompressedFloatFormat::U8U8U8, + Format::RGBA => UncompressedFloatFormat::U8U8U8U8, + _ => return None, + }; + let opengl_texture = Texture2d::empty_with_format( &self.display, format, MipmapsOption::NoMipmap, - surface_dimensions.0, - surface_dimensions.1, + images.width, + images.height, ).unwrap(); - self.display.get_context().exec_in_context(|| { - self.display.borrow().egl_image_to_texture(image, opengl_texture.get_id()); - }); - opengl_texture + if let Err(_) = unsafe { self.display.get_context().exec_in_context(|| { + images.bind_to_texture(0, opengl_texture.get_id()) + }) } { return None }; + Some(opengl_texture) } - pub fn render_texture(&self, target: &mut glium::Frame, texture: Texture2d, + 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 mut yscale = -2.0 * (surface_dimensions.1 as f32) / (screen_size.1 as f32); - if y_inverted { - yscale = -yscale; - } 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 mut y = 1.0 - 2.0 * (surface_location.1 as f32) / (screen_size.1 as f32); + + if y_inverted { + yscale = -yscale; + y -= surface_dimensions.1 as f32; + } let uniforms = uniform! { matrix: [ @@ -155,7 +163,7 @@ impl GliumDrawer { [ 0.0 , 0.0 , 1.0, 0.0], [ x , y , 0.0, 1.0] ], - tex: &texture, + tex: texture, }; target @@ -164,10 +172,28 @@ impl GliumDrawer { &self.index_buffer, &self.program, &uniforms, - &Default::default(), + &glium::DrawParameters { + blend: glium::Blend { + color: glium::BlendingFunction::Addition { + source: glium::LinearBlendingFactor::One, + destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + alpha: glium::BlendingFunction::Addition { + source: glium::LinearBlendingFactor::One, + destination: glium::LinearBlendingFactor::OneMinusSourceAlpha, + }, + ..Default::default() + }, + depth: glium::Depth { + test: glium::DepthTest::IfLess, + write: false, + ..Default::default() + }, + .. Default::default() + }, ) .unwrap(); - } + } #[inline] pub fn draw(&self) -> Frame { diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs index 746d294..9d8b4ba 100644 --- a/examples/helpers/implementations.rs +++ b/examples/helpers/implementations.rs @@ -1,78 +1,81 @@ use super::{GliumDrawer, WindowMap}; -use glium::texture::{Texture2d, UncompressedFloatFormat}; +use smithay::backend::graphics::egl::wayland::{Format, BufferAccessError}; +use glium::texture::Texture2d; use rand; -use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImage}; +use smithay::backend::graphics::egl::{EGLGraphicsBackend, EGLImages}; use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttributes, SurfaceUserImplementation}; use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure}; use smithay::wayland::shm::with_buffer_contents as shm_buffer_contents; -use smithay::wayland::drm::{with_buffer_contents as drm_buffer_contents, Attributes, Format}; use std::cell::RefCell; use std::rc::Rc; +use std::borrow::Borrow; use wayland_server::{EventLoop, StateToken}; define_roles!(Roles => [ ShellSurface, ShellSurfaceRole ] ); #[derive(Default)] pub struct SurfaceData { - pub texture: Option, pub buffer: Option, + pub texture: Option, } pub enum Buffer { - Egl { images: Vec, attributes: Attributes }, + Egl { images: EGLImages }, Shm { data: Vec, size: (u32, u32) }, } unsafe impl Send for Buffer {} -pub fn surface_implementation() -> SurfaceUserImplementation> { +pub fn surface_implementation() -> SurfaceUserImplementation>> { SurfaceUserImplementation { commit: |_, drawer, surface, token| { // we retrieve the contents of the associated buffer and copy it token.with_surface_data(surface, |attributes| { - match attributes.buffer() { - Some(ref buffer) => { - // we ignore hotspot coordinates in this simple example (`attributes.buffer_coordinates()`) - 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 }); - }).is_err() { - shm_buffer_contents(&buffer, |slice, data| { - let offset = data.offset as usize; - let stride = data.stride as usize; - let width = data.width as usize; - let height = data.height as usize; - let mut new_vec = Vec::with_capacity(width * height * 4); - for i in 0..height { - new_vec - .extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]); - } - attributes.user_data.texture = Some(drawer.texture_from_mem(&new_vec, data.width as u32, data.height as u32)); - attributes.user_data.buffer = - Some(Buffer::Shm { data: new_vec, position: (data.width as u32, data.height as u32) }); - }).unwrap(); + match attributes.buffer.take() { + Some(Some((buffer, (_x, _y)))) => { + // we ignore hotspot coordinates in this simple example + match as Borrow>::borrow(&**drawer).egl_buffer_contents(buffer) { + Ok(images) => { + let format = match images.format { + Format::RGB => {}, + Format::RGBA => {}, + _ => { + // we don't handle the more complex formats here. + attributes.user_data.buffer = None; + attributes.user_data.texture = None; + return; + }, + }; + attributes.user_data.texture = drawer.texture_from_egl(&images); + attributes.user_data.buffer = Some(Buffer::Egl { images }); + }, + Err(BufferAccessError::NotManaged(buffer)) => { + shm_buffer_contents(&buffer, |slice, data| { + let offset = data.offset as usize; + let stride = data.stride as usize; + let width = data.width as usize; + let height = data.height as usize; + let mut new_vec = Vec::with_capacity(width * height * 4); + for i in 0..height { + new_vec + .extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]); + } + attributes.user_data.texture = Some(drawer.texture_from_mem(&new_vec, (data.width as u32, data.height as u32))); + attributes.user_data.buffer = Some(Buffer::Shm { data: new_vec, size: (data.width as u32, data.height as u32) }); + }).unwrap(); + buffer.release(); + }, + Err(err) => panic!("EGL error: {}", err), } } - None => { + Some(None) => { // erase the contents attributes.user_data.buffer = None; + attributes.user_data.texture = None; } + None => {} } }); }, @@ -82,14 +85,15 @@ pub fn surface_implementation() -> SurfaceUserI } } -pub struct ShellIData { - pub token: CompositorToken, - pub window_map: Rc>>, +pub struct ShellIData { + pub token: CompositorToken>>, + pub window_map: Rc>, (), F>>>, } -pub fn shell_implementation() -> ShellSurfaceUserImplementation, ()> +pub fn shell_implementation() -> ShellSurfaceUserImplementation>, ShellIData, ()> where F: Fn(&SurfaceAttributes) -> Option<(i32, i32)>, + G: EGLGraphicsBackend + 'static, { ShellSurfaceUserImplementation { new_client: |_, _, _| {}, @@ -134,23 +138,27 @@ fn get_size(attrs: &SurfaceAttributes) -> Option<(i32, i32)> { .user_data .buffer .as_ref() - .map(|&(_, (w, h))| (w as i32, h as i32)) + .map(|ref buffer| match **buffer { + Buffer::Shm { ref size, .. } => *size, + Buffer::Egl { ref images } => (images.width, images.height), + }) + .map(|(x, y)| (x as i32, y as i32)) } -pub type MyWindowMap = WindowMap< +pub type MyWindowMap = WindowMap< SurfaceData, Roles, - (), + Rc>, (), fn(&SurfaceAttributes) -> Option<(i32, i32)>, >; -pub fn init_shell( - evl: &mut EventLoop, log: ::slog::Logger, data: ID) +pub fn init_shell( + evl: &mut EventLoop, log: ::slog::Logger, data: Rc>) -> ( - CompositorToken, - StateToken>, - Rc>, + CompositorToken>>, + StateToken>, ()>>, + Rc>>, ) { let (compositor_token, _, _) = compositor_init(evl, surface_implementation(), data, log.clone()); diff --git a/examples/winit.rs b/examples/winit.rs index 39ff869..632e86a 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -27,23 +27,23 @@ use std::cell::RefCell; use std::rc::Rc; use wayland_server::protocol::{wl_output, wl_pointer}; -struct WinitInputHandler { +struct WinitInputHandler { log: Logger, pointer: PointerHandle, keyboard: KeyboardHandle, - window_map: Rc>, + window_map: Rc>>, pointer_location: (f64, f64), serial: u32, } -impl WinitInputHandler { +impl WinitInputHandler { fn next_serial(&mut self) -> u32 { self.serial += 1; self.serial } } -impl InputHandler for WinitInputHandler { +impl InputHandler for WinitInputHandler { fn on_seat_created(&mut self, _: &input::Seat) { /* never happens with winit */ } @@ -140,6 +140,7 @@ fn main() { info!(log, "EGL hardware-acceleration enabled"); } + let (w, h) = renderer.get_framebuffer_dimensions(); let drawer = Rc::new(GliumDrawer::from(renderer)); /* @@ -172,7 +173,6 @@ fn main() { log.clone(), ); - let (w, h) = renderer.get_framebuffer_dimensions(); event_loop .state() .get_mut(&output_token) @@ -194,10 +194,6 @@ fn main() { refresh: 60_000, }); - /* - * Initialize glium - */ - input.set_handler(WinitInputHandler { log: log.clone(), pointer, @@ -217,7 +213,7 @@ fn main() { input.dispatch_new_events().unwrap(); let mut frame = drawer.draw(); - frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, None, None); + frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None); // redraw the frame, in a simple but inneficient way { let screen_dimensions = drawer.get_framebuffer_dimensions(); @@ -232,7 +228,7 @@ fn main() { initial_place, |_surface, attributes, role, &(mut x, mut y)| { // there is actually something to draw ! - if let Some(texture) = attributes.user_data.texture { + if let Some(ref texture) = attributes.user_data.texture { if let Ok(subdata) = Role::::data(role) { x += subdata.x; y += subdata.y; @@ -241,11 +237,11 @@ fn main() { &mut frame, texture, match *attributes.user_data.buffer.as_ref().unwrap() { - Buffer::Egl { ref attributes, .. } => attributes.y_inverted, + Buffer::Egl { ref images } => images.y_inverted, Buffer::Shm { .. } => false, }, match *attributes.user_data.buffer.as_ref().unwrap() { - Buffer::Egl { ref attributes, .. } => (attributes.width, attributes.height), + Buffer::Egl { ref images } => (images.width, images.height), Buffer::Shm { ref size, .. } => *size, }, (x, y), diff --git a/src/backend/graphics/egl/context.rs b/src/backend/graphics/egl/context.rs index a09053f..9e84fd8 100644 --- a/src/backend/graphics/egl/context.rs +++ b/src/backend/graphics/egl/context.rs @@ -132,6 +132,12 @@ impl> EGLContext { }; ffi::egl::LOAD.call_once(|| { + fn constrain(f: F) -> F + where + F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void, + { + f + }; ffi::egl::load_with(|sym| { let name = CString::new(sym).unwrap(); let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes()); @@ -140,14 +146,16 @@ impl> EGLContext { 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(), - } + let procAddress = constrain(|sym| { + let addr = CString::new(sym).unwrap(); + let addr = addr.as_ptr(); + ffi::egl::GetProcAddress(addr) as *const _ }); + ffi::egl::load_with(&procAddress); + ffi::egl::BindWaylandDisplayWL::load_with(&procAddress); + ffi::egl::UnbindWaylandDisplayWL::load_with(&procAddress); + ffi::egl::QueryWaylandBufferWL::load_with(&procAddress); + ffi::gl::load_with(&procAddress); }); // the first step is to query the list of extensions without any display, if supported @@ -434,6 +442,23 @@ impl> EGLContext { info!(log, "EGL context created"); + // make current and get list of gl extensions + ffi::egl::MakeCurrent( + display as *const _, + ptr::null(), + ptr::null(), + context as *const _, + ); + + // the list of gl extensions supported by the context + let gl_extensions = { + let data = CStr::from_ptr(ffi::gl::GetString(ffi::gl::EXTENSIONS) as *const _).to_bytes().to_vec(); + let list = String::from_utf8(data).unwrap(); + list.split(' ').map(|e| e.to_string()).collect::>() + }; + + info!(log, "GL Extensions: {:?}", gl_extensions); + Ok(( Rc::new(context as *const _), Rc::new(display as *const _), @@ -441,7 +466,7 @@ impl> EGLContext { 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"), + gl_extensions.iter().any(|s| *s == "GL_OES_EGL_image" || *s == "GL_OES_EGL_image_base"), )) } @@ -530,7 +555,7 @@ pub enum GlProfile { } /// Describes how the backend should choose a pixel format. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] 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`. @@ -557,3 +582,19 @@ pub struct PixelFormatRequirements { /// The default is `false`. pub stereoscopy: bool, } + +impl Default for PixelFormatRequirements { + fn default() -> Self { + PixelFormatRequirements { + hardware_accelerated: Some(true), + color_bits: Some(24), + float_color_buffer: false, + alpha_bits: Some(8), + depth_bits: Some(24), + stencil_bits: Some(8), + double_buffer: Some(true), + multisampling: None, + stereoscopy: false, + } + } +} diff --git a/src/backend/graphics/egl/error.rs b/src/backend/graphics/egl/error.rs index 9775cf9..5c9419e 100644 --- a/src/backend/graphics/egl/error.rs +++ b/src/backend/graphics/egl/error.rs @@ -47,11 +47,6 @@ error_chain! { description("Failed to create a new EGLSurface") } - #[doc = "The OpenGL context has been lost and needs to be recreated"] - ContextLost { - description("The OpenGL context has been lost and needs to be recreated") - } - #[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"), @@ -74,11 +69,6 @@ error_chain! { description("Index of plane is out of bounds for EGLImages") } - #[doc = "This buffer is not mananged by EGL"] - BufferNotManaged { - description("This buffer is not mananged by EGL") - } - #[doc = "Failed to create EGLImages from the buffer"] EGLImageCreationFailed { description("Failed to create EGLImages from the buffer") diff --git a/src/backend/graphics/egl/mod.rs b/src/backend/graphics/egl/mod.rs index 2c2ca33..3ed149f 100644 --- a/src/backend/graphics/egl/mod.rs +++ b/src/backend/graphics/egl/mod.rs @@ -9,8 +9,8 @@ use super::GraphicsBackend; use nix::libc::c_void; use std::fmt; use wayland_server::Display; +use wayland_server::protocol::wl_buffer::WlBuffer; -pub use self::ffi::egl::types::EGLImage; pub mod context; pub use self::context::EGLContext; pub mod error; @@ -20,6 +20,7 @@ pub mod native; pub mod surface; pub use self::surface::EGLSurface; pub mod wayland; +pub use self::wayland::{EGLImages, BufferAccessError}; /// Error that can happen when swapping buffers. #[derive(Debug, Clone, PartialEq)] @@ -182,5 +183,5 @@ pub trait EGLGraphicsBackend: GraphicsBackend { /// `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>; + fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result; } diff --git a/src/backend/graphics/egl/wayland.rs b/src/backend/graphics/egl/wayland.rs index 8874018..ac57d46 100644 --- a/src/backend/graphics/egl/wayland.rs +++ b/src/backend/graphics/egl/wayland.rs @@ -1,10 +1,114 @@ -use backend::graphics::egl::{EGLContext, EGLImage, ffi, native}; +use backend::graphics::egl::{EGLContext, EglExtensionNotSupportedError, ffi, native}; use backend::graphics::egl::error::*; +use backend::graphics::egl::ffi::egl::types::EGLImage; use nix::libc::{c_uint}; use std::rc::{Rc, Weak}; +use std::fmt; use wayland_server::{Display, Resource}; use wayland_server::protocol::wl_buffer::WlBuffer; +/// Error that can occur when accessing an EGL buffer +pub enum BufferAccessError { + /// This buffer is not managed by the EGL buffer + NotManaged(WlBuffer), + /// Failed to create EGLImages from the buffer + EGLImageCreationFailed, + /// The required EGL extension is not supported by the underlying EGL implementation + EglExtensionNotSupported(EglExtensionNotSupportedError), +} + +impl fmt::Debug for BufferAccessError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + match *self { + BufferAccessError::NotManaged(_) => write!(formatter, "BufferAccessError::NotManaged"), + BufferAccessError::EGLImageCreationFailed => write!(formatter, "BufferAccessError::EGLImageCreationFailed"), + BufferAccessError::EglExtensionNotSupported(ref err) => write!(formatter, "{:?}", err), + } + } +} + +impl fmt::Display for BufferAccessError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use ::std::error::Error; + match *self { + BufferAccessError::NotManaged(_) => write!(formatter, "{}", self.description()), + BufferAccessError::EGLImageCreationFailed => write!(formatter, "{}", self.description()), + BufferAccessError::EglExtensionNotSupported(ref err) => err.fmt(formatter), + } + } +} + +impl ::std::error::Error for BufferAccessError { + fn description(&self) -> &str { + match *self { + BufferAccessError::NotManaged(_) => "This buffer is not mananged by EGL", + BufferAccessError::EGLImageCreationFailed => "Failed to create EGLImages from the buffer", + BufferAccessError::EglExtensionNotSupported(ref err) => err.description(), + } + } + + fn cause(&self) -> Option<&::std::error::Error> { + match *self { + BufferAccessError::EglExtensionNotSupported(ref err) => Some(err), + _ => None, + } + } +} + +impl From for BufferAccessError { + fn from(error: EglExtensionNotSupportedError) -> Self { + BufferAccessError::EglExtensionNotSupported(error) + } +} + +/// Error that might happen when binding an EGLImage to a gl texture +#[derive(Debug, Clone, PartialEq)] +pub enum TextureCreationError { + /// The given plane index is out of bounds + PlaneIndexOutOfBounds, + /// 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, + /// Failed to bind the EGLImage to the given texture + /// + /// The given argument is the gl error code + TextureBindingFailed(u32), +} + +impl fmt::Display for TextureCreationError { + fn fmt(&self, formatter: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { + use ::std::error::Error; + match *self { + TextureCreationError::ContextLost => write!(formatter, "{}", self.description()), + TextureCreationError::PlaneIndexOutOfBounds => write!(formatter, "{}", self.description()), + TextureCreationError::TextureBindingFailed(code) => write!(formatter, "{}. Gl error code: {:?}", self.description(), code), + } + } +} + +impl ::std::error::Error for TextureCreationError { + fn description(&self) -> &str { + match *self { + TextureCreationError::ContextLost => "The context has been lost, it needs to be recreated", + TextureCreationError::PlaneIndexOutOfBounds => "This buffer is not mananged by EGL", + TextureCreationError::TextureBindingFailed(_) => "Failed to create EGLImages from the buffer", + } + } + + fn cause(&self) -> Option<&::std::error::Error> { + None + } +} + #[repr(i32)] pub enum Format { RGB = ffi::egl::TEXTURE_RGB as i32, @@ -40,15 +144,20 @@ impl EGLImages { self.format.num_planes() } - pub unsafe fn bind_to_tex(&self, plane: usize, tex_id: c_uint) -> Result<()> { + pub unsafe fn bind_to_texture(&self, plane: usize, tex_id: c_uint) -> ::std::result::Result<(), TextureCreationError> { if self.display.upgrade().is_some() { - ffi::gl::EGLImageTargetTexture2DOES(tex_id, *self.images.get(plane).chain_err(|| ErrorKind::PlaneIndexOutOfBounds)?); - match ffi::egl::GetError() as u32 { - ffi::gl::NO_ERROR => Ok(()), - err => bail!(ErrorKind::Unknown(err)), - } + let mut old_tex_id: i32 = 0; + ffi::gl::GetIntegerv(ffi::gl::TEXTURE_BINDING_2D, &mut old_tex_id); + ffi::gl::BindTexture(ffi::gl::TEXTURE_2D, tex_id); + ffi::gl::EGLImageTargetTexture2DOES(ffi::gl::TEXTURE_2D, *self.images.get(plane).ok_or(TextureCreationError::PlaneIndexOutOfBounds)?); + let res = match ffi::egl::GetError() as u32 { + ffi::egl::SUCCESS => Ok(()), + err => Err(TextureCreationError::TextureBindingFailed(err)), + }; + ffi::gl::BindTexture(ffi::gl::TEXTURE_2D, old_tex_id as u32); + res } else { - bail!(ErrorKind::ContextLost) + Err(TextureCreationError::ContextLost) } } } @@ -60,6 +169,7 @@ impl Drop for EGLImages { unsafe { ffi::egl::DestroyImageKHR(*display, image); } } } + self.buffer.release(); } } @@ -111,14 +221,17 @@ impl> EGLContext { Ok(()) } - pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> Result { + pub fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result { + if !self.wl_drm_support { + return Err(EglExtensionNotSupportedError(&["EGL_WL_bind_wayland_display"]).into()); + } if !self.egl_to_texture_support { - bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"])); + return Err(EglExtensionNotSupportedError(&["GL_OES_EGL_image"]).into()); } let mut format: i32 = 0; if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::EGL_TEXTURE_FORMAT, &mut format as *mut _) == 0 } { - bail!(ErrorKind::BufferNotManaged); + return Err(BufferAccessError::NotManaged(buffer)); } let format = match format { x if x == ffi::egl::TEXTURE_RGB as i32 => Format::RGB, @@ -132,16 +245,16 @@ impl> EGLContext { let mut width: i32 = 0; if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WIDTH as i32, &mut width as *mut _) == 0 } { - bail!(ErrorKind::BufferNotManaged); + return Err(BufferAccessError::NotManaged(buffer)); } let mut height: i32 = 0; if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::HEIGHT as i32, &mut height as *mut _) == 0 } { - bail!(ErrorKind::BufferNotManaged); + return Err(BufferAccessError::NotManaged(buffer)); } let mut inverted: i32 = 0; - if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) == 0 } { + if unsafe { ffi::egl::QueryWaylandBufferWL(*self.display, buffer.ptr() as *mut _, ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) != 0 } { inverted = 1; } @@ -157,12 +270,12 @@ impl> EGLContext { unsafe { ffi::egl::CreateImageKHR( *self.display, ffi::egl::NO_CONTEXT, - ffi::egl::WAYLAND_BUFFER_WL, + ffi::egl::WAYLAND_BUFFER_WL, buffer.ptr() as *mut _, out.as_ptr(), ) }; if image == ffi::egl::NO_IMAGE_KHR { - bail!(ErrorKind::EGLImageCreationFailed); + return Err(BufferAccessError::EGLImageCreationFailed); } else { image } diff --git a/src/backend/winit.rs b/src/backend/winit.rs index a2d2f18..1dd4add 100644 --- a/src/backend/winit.rs +++ b/src/backend/winit.rs @@ -1,7 +1,7 @@ //! Implementation of backend traits for types provided by `winit` use backend::graphics::GraphicsBackend; -use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError}; +use backend::graphics::egl::{EGLGraphicsBackend, EGLContext, EGLSurface, BufferAccessError, PixelFormat, SwapBuffersError, EglExtensionNotSupportedError, EGLImages}; use backend::graphics::egl::error as egl_error; use backend::graphics::egl::native; use backend::graphics::egl::context::{GlAttributes, PixelFormatRequirements}; @@ -18,6 +18,7 @@ use winit::{ElementState, Event, EventsLoop, KeyboardInput, MouseButton as Winit MouseScrollDelta, Touch, TouchPhase, WindowBuilder, WindowEvent, Window as WinitWindow}; use wayland_client::egl as wegl; use wayland_server::Display; +use wayland_server::protocol::wl_buffer::WlBuffer; error_chain! { errors { @@ -134,13 +135,7 @@ where .chain_err(|| ErrorKind::InitFailed)?; debug!(log, "Window created"); - let reqs = PixelFormatRequirements { - hardware_accelerated: Some(true), - color_bits: Some(24), - alpha_bits: Some(8), - ..Default::default() - }; - + let reqs = Default::default(); let window = Rc::new( if native::NativeDisplay::::is_backend(&winit_window) { let context = EGLContext::::new( @@ -277,12 +272,12 @@ impl EGLGraphicsBackend for WinitGraphicsBackend { 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(()) + fn egl_buffer_contents(&self, buffer: WlBuffer) -> ::std::result::Result { + match *self.window { + Window::Wayland { ref context, .. } => context.egl_buffer_contents(buffer), + Window::X11 { ref context, .. } => context.egl_buffer_contents(buffer), + } } - */ } /// Errors that may happen when driving the event loop of `WinitInputBackend` diff --git a/src/wayland/compositor/mod.rs b/src/wayland/compositor/mod.rs index 2265d6c..0747dc8 100644 --- a/src/wayland/compositor/mod.rs +++ b/src/wayland/compositor/mod.rs @@ -72,9 +72,7 @@ //! //! As you can see in the previous example, in the end we are retrieving a token from //! the init function. This token is necessary to retrieve the metadata associated with -//! a surface. It can be cloned, and is sendable accross threads (as long as your -//! `Mydata` and `MyRoles` are `Send` too). See `CompositorToken` for -//! the details of what it enables you. +//! a surface. It can be cloned. See `CompositorToken` for the details of what it enables you. //! //! The surface metadata is held in the `SurfaceAttributes` struct. In contains double-buffered //! state pending from the client as defined by the protocol for `wl_surface`, as well as your @@ -248,9 +246,6 @@ pub struct CompositorToken { _idata: ::std::marker::PhantomData<*mut ID>, } -unsafe impl Send for CompositorToken {} -unsafe impl Sync for CompositorToken {} - // we implement them manually because #[derive(..)] would require // U: Clone and R: Clone impl Copy for CompositorToken {} @@ -270,7 +265,7 @@ impl CompositorToken { } } -impl CompositorToken { +impl CompositorToken { /// Access the data of a surface /// /// The closure will be called with the contents of the data associated with this surface.