Start EGL wl_buffer image retrieval

This commit is contained in:
Drakulix 2017-12-10 22:09:17 +01:00
parent 14fc36bf54
commit 5c846d550c
6 changed files with 226 additions and 23 deletions

View File

@ -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();
}

View File

@ -106,7 +106,7 @@ impl<T: Into<GliumGraphicsBackend<T>> + EGLGraphicsBackend + 'static> From<T> fo
}
impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
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<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
.unwrap();
}
pub fn render_egl(&self, target: &mut glium::Frame, images: Vec<EGLImage>,
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()

View File

@ -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<u8>, (u32, u32))>,
}
pub enum Buffer {
Egl { images: Vec<EGLImage>, attributes: Attributes },
Shm { data: Vec<u8>, size: (u32, u32) },
}
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
SurfaceUserImplementation {
commit: |_, _, surface, token| {
@ -24,7 +30,10 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
match attributes.buffer.take() {
Some(Some((buffer, (_x, _y)))) => {
// we ignore hotspot coordinates in this simple example
with_buffer_contents(&buffer, |slice, data| {
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;
@ -35,8 +44,9 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
.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)));
Some(Buffer::Shm { data: new_vec, position: (data.width as u32, data.height as u32) });
}).unwrap();
}
buffer.release();
}
Some(None) => {

View File

@ -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::<SubsurfaceRole>::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::<SubsurfaceRole>::data(role) {
x += subdata.x;
y += subdata.y;
}
drawer.render_shm(
&mut frame,
data,
(w, h),
(x, y),
screen_dimensions,

View File

@ -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<c_int>,
pixel_format: PixelFormat,
backend_type: NativeType,
wl_drm_support: bool,
pub(crate) wl_drm_support: bool,
logger: slog::Logger,
_lifetime: PhantomData<&'a ()>,
_type: PhantomData<T>,
@ -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> {}

99
src/wayland/drm/mod.rs Normal file
View File

@ -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<F>(buffer: &WlBuffer, context: &EGLContext, f: F) -> Result<(), BufferAccessError>
where
F: FnOnce(Attributes, Vec<EGLImage>)
{
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)
}