diff --git a/build.rs b/build.rs index 7b556f9..cc83ec8 100644 --- a/build.rs +++ b/build.rs @@ -33,4 +33,16 @@ fn main() { ], ).write_bindings(gl_generator::GlobalGenerator, &mut file) .unwrap(); + + let mut file = File::create(&dest.join("gl_bindings.rs")).unwrap(); + Registry::new( + Api::Gles2, + (3, 2), + Profile::Compatibility, + Fallbacks::None, + [ + "GL_OES_EGL_image", + ], + ).write_bindings(gl_generator::GlobalGenerator, &mut file) + .unwrap(); } diff --git a/examples/helpers/glium.rs b/examples/helpers/glium.rs index 908db89..ec35656 100644 --- a/examples/helpers/glium.rs +++ b/examples/helpers/glium.rs @@ -106,7 +106,7 @@ impl> + EGLGraphicsBackend + 'static> From fo } impl GliumDrawer { - pub fn render(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), + pub fn render_shm(&self, target: &mut glium::Frame, contents: &[u8], surface_dimensions: (u32, u32), surface_location: (i32, i32), screen_size: (u32, u32)) { let image = glium::texture::RawImage2d { data: contents.into(), @@ -143,6 +143,51 @@ impl GliumDrawer { .unwrap(); } + pub fn render_egl(&self, target: &mut glium::Frame, images: Vec, + format: UncompressedFloatFormat, y_inverted: bool, surface_dimensions: (u32, u32), + surface_location: (i32, i32), screen_size: (u32, u32)) + { + let opengl_texture = glium::texture::Texture2d::empty_with_format( + &self.display, + MipmapsOption::NoMipmap, + format, + surface_dimensions.0, + surface_dimensions.1, + ).unwrap(); + self.display.exec_in_context(|| { + self.display.borrow().egl_image_to_texture(images[0], opengl_texture.get_id()); + }); + + 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 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(); + } + #[inline] pub fn draw(&self) -> Frame { self.display.draw() diff --git a/examples/helpers/implementations.rs b/examples/helpers/implementations.rs index 920034b..8cc3df2 100644 --- a/examples/helpers/implementations.rs +++ b/examples/helpers/implementations.rs @@ -4,7 +4,8 @@ use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttr SurfaceUserImplementation}; use smithay::wayland::shell::{shell_init, PopupConfigure, ShellState, ShellSurfaceRole, ShellSurfaceUserImplementation, ToplevelConfigure}; -use smithay::wayland::shm::with_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 std::cell::RefCell; use std::rc::Rc; use wayland_server::{EventLoop, StateToken}; @@ -16,6 +17,11 @@ pub struct SurfaceData { pub buffer: Option<(Vec, (u32, u32))>, } +pub enum Buffer { + Egl { images: Vec, attributes: Attributes }, + Shm { data: Vec, size: (u32, u32) }, +} + pub fn surface_implementation() -> SurfaceUserImplementation { SurfaceUserImplementation { commit: |_, _, surface, token| { @@ -24,19 +30,23 @@ pub fn surface_implementation() -> SurfaceUserImplementation { // we ignore hotspot coordinates in this simple example - with_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.buffer = - Some((new_vec, (data.width as u32, data.height as u32))); - }).unwrap(); + if let Ok(_) = drm_buffer_contents(&buffer, |attributes, images| { + attributes.user_data.buffer = Some(Buffer::Egl { images, attributes }); + }) {} else { + 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.buffer = + Some(Buffer::Shm { data: new_vec, position: (data.width as u32, data.height as u32) }); + }).unwrap(); + } buffer.release(); } Some(None) => { diff --git a/examples/winit.rs b/examples/winit.rs index 90390db..f19e867 100644 --- a/examples/winit.rs +++ b/examples/winit.rs @@ -12,7 +12,8 @@ extern crate wayland_server; mod helpers; use glium::Surface; -use helpers::{init_shell, GliumDrawer, MyWindowMap}; +use glium::texture::UncompressedFloatFormat; +use helpers::{init_shell, GliumDrawer, MyWindowMap, Buffer}; use slog::{Drain, Logger}; use smithay::backend::graphics::egl::EGLGraphicsBackend; use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent, @@ -226,15 +227,35 @@ fn main() { wl_surface, initial_place, |_surface, attributes, role, &(mut x, mut y)| { - if let Some((ref contents, (w, h))) = attributes.user_data.buffer { + if let Some(Buffer::Egl { images, attributes }) = attributes.user_data.buffer { // there is actually something to draw ! if let Ok(subdata) = Role::::data(role) { x += subdata.x; y += subdata.y; } - drawer.render( + drawer.render_egl( &mut frame, - contents, + images, + format: match attributes.format { + Format::RGB => UncompressedFloatFormat::U8U8U8, + Format::RGBA => UncompressedFloatFormat::U8U8U8U8, + _ => unimplemented!(), + }, + 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::::data(role) { + x += subdata.x; + y += subdata.y; + } + drawer.render_shm( + &mut frame, + data, (w, h), (x, y), screen_dimensions, diff --git a/src/backend/graphics/egl.rs b/src/backend/graphics/egl.rs index a11a0e4..8470fd1 100644 --- a/src/backend/graphics/egl.rs +++ b/src/backend/graphics/egl.rs @@ -8,7 +8,7 @@ use super::GraphicsBackend; #[cfg(feature = "backend_drm")] use gbm::{AsRaw, Device as GbmDevice, Surface as GbmSurface}; -use nix::libc::{c_int, c_void}; +use nix::libc::{c_int, c_uint, c_void}; use rental::TryNewError; use slog; use std::error; @@ -27,7 +27,7 @@ use winit::os::unix::WindowExt; use wayland_server::Display; #[allow(non_camel_case_types, dead_code, unused_mut)] -mod ffi { +pub(crate) mod ffi { use nix::libc::{c_long, c_void, int32_t, uint64_t}; pub type khronos_utime_nanoseconds_t = khronos_uint64_t; @@ -41,6 +41,10 @@ mod ffi { pub type NativePixmapType = *const c_void; pub type NativeWindowType = *const c_void; + pub mod gl { + include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs")); + } + pub mod egl { use super::*; use libloading::Library; @@ -358,12 +362,12 @@ unsafe impl NativeSurface for () { /// EGL context for rendering pub struct EGLContext<'a, T: NativeSurface> { context: ffi::egl::types::EGLContext, - display: ffi::egl::types::EGLDisplay, + pub(crate) display: ffi::egl::types::EGLDisplay, config_id: ffi::egl::types::EGLConfig, surface_attributes: Vec, pixel_format: PixelFormat, backend_type: NativeType, - wl_drm_support: bool, + pub(crate) wl_drm_support: bool, logger: slog::Logger, _lifetime: PhantomData<&'a ()>, _type: PhantomData, @@ -472,6 +476,14 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> { 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 @@ -947,6 +959,10 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> { } Ok(()) } + + pub fn egl_image_to_texture(&self, image: ffi::EGLImage, tex_id: c_uint) { + ffi::gl::EGLImageTargetTexture2DOES(tex_id, image); + } } unsafe impl<'a, T: NativeSurface> Send for EGLContext<'a, T> {} diff --git a/src/wayland/drm/mod.rs b/src/wayland/drm/mod.rs new file mode 100644 index 0000000..e270e17 --- /dev/null +++ b/src/wayland/drm/mod.rs @@ -0,0 +1,99 @@ +use ::backend::graphics::egl::ffi; +use ::backend::graphics::egl::EGLContext; +pub use ::backend::graphics::egl::ffi::EGLImage; +use nix::libc::c_int; +use wayland_server::protocol::wl_buffer::WlBuffer; + +/// Error that can occur when accessing an EGL buffer +#[derive(Debug)] +pub enum BufferAccessError { + /// This buffer is not managed by EGL + NotManaged, + /// Failed to create EGLImages from the buffer + FailedToCreateEGLImage, +} + +#[repr(u32)] +pub enum Format { + RGB = ffi::TEXTURE_RGB, + RGBA = ffi::TEXTURE_RGBA, + External = ffi::TEXTURE_EXTERNAL_WL, + Y_UV = ffi::TEXTURE_Y_UV_WL, + Y_U_V = ffi::TEXTURE_Y_U_V_WL, + Y_XUXV = ffi::TEXTURE_Y_XUXV_WL, +} + +impl Format { + pub fn num_planes(&self) -> u32 { + match *self { + Format::RGB | Format::RGBA | Format::External => 1, + Format::Y_UV | Format::Y_XUXV => 2, + Format::Y_U_V => 3, + } + } +} + +pub struct Attributes { + width: u32, + height: u32, + y_inverted: bool, + format: Format, +} + +pub fn with_buffer_contents(buffer: &WlBuffer, context: &EGLContext, f: F) -> Result<(), BufferAccessError> +where + F: FnOnce(Attributes, Vec) +{ + let mut format: u32 = 0; + if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::TEXTURE_FORMAT, &mut format as *mut _) == 0 { + return Err(BufferAccessError::NotManaged); + } + + let mut width: u32 = 0; + if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::WIDTH, &mut width as *mut _) == 0 { + return Err(BufferAccessError::NotManaged); + } + + let mut height: u32 = 0; + if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::HEIGHT, &mut height as *mut _) == 0 { + return Err(BufferAccessError::NotManaged); + } + + let mut inverted: u32 = 0; + if context.egl.QueryWaylandBufferWL(context.display, buffer.ptr(), ffi::egl::WAYLAND_Y_INVERTED_WL, &mut inverted as *mut _) == 0 { + inverted = 1; + } + + let attributes = Attributes { + width, + 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); + out.push(ffi::egl::WAYLAND_PLANE_WL as i32); + out.push(i as i32); + out.push(ffi::egl::NONE as i32); + + images.push({ + let image = + ffi::egl::CreateImageKHR( + context.display, + ffi::egl::NO_CONTEXT, + ffi::egl::WAYLAND_BUFFER_WL, + buffer.ptr(), + out.as_ptr(), + ); + if image == ffi::egl::NO_IMAGE_KHR { + return Err(BufferAccessError::FailedToCreateEGLImage); + } else { + image + } + }); + } + + f(attributes, images) +}