Start EGL wl_buffer image retrieval
This commit is contained in:
parent
14fc36bf54
commit
5c846d550c
12
build.rs
12
build.rs
|
@ -33,4 +33,16 @@ fn main() {
|
||||||
],
|
],
|
||||||
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
).write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||||
.unwrap();
|
.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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,7 +106,7 @@ 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(&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)) {
|
surface_location: (i32, i32), screen_size: (u32, u32)) {
|
||||||
let image = glium::texture::RawImage2d {
|
let image = glium::texture::RawImage2d {
|
||||||
data: contents.into(),
|
data: contents.into(),
|
||||||
|
@ -143,6 +143,51 @@ impl<F: EGLGraphicsBackend + 'static> GliumDrawer<F> {
|
||||||
.unwrap();
|
.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]
|
#[inline]
|
||||||
pub fn draw(&self) -> Frame {
|
pub fn draw(&self) -> Frame {
|
||||||
self.display.draw()
|
self.display.draw()
|
||||||
|
|
|
@ -4,7 +4,8 @@ use smithay::wayland::compositor::{compositor_init, CompositorToken, SurfaceAttr
|
||||||
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;
|
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::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wayland_server::{EventLoop, StateToken};
|
use wayland_server::{EventLoop, StateToken};
|
||||||
|
@ -16,6 +17,11 @@ pub struct SurfaceData {
|
||||||
pub buffer: Option<(Vec<u8>, (u32, u32))>,
|
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, ()> {
|
pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles, ()> {
|
||||||
SurfaceUserImplementation {
|
SurfaceUserImplementation {
|
||||||
commit: |_, _, surface, token| {
|
commit: |_, _, surface, token| {
|
||||||
|
@ -24,7 +30,10 @@ pub fn surface_implementation() -> SurfaceUserImplementation<SurfaceData, Roles,
|
||||||
match attributes.buffer.take() {
|
match attributes.buffer.take() {
|
||||||
Some(Some((buffer, (_x, _y)))) => {
|
Some(Some((buffer, (_x, _y)))) => {
|
||||||
// we ignore hotspot coordinates in this simple example
|
// 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 offset = data.offset as usize;
|
||||||
let stride = data.stride as usize;
|
let stride = data.stride as usize;
|
||||||
let width = data.width 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)]);
|
.extend(&slice[(offset + i * stride)..(offset + i * stride + width * 4)]);
|
||||||
}
|
}
|
||||||
attributes.user_data.buffer =
|
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();
|
}).unwrap();
|
||||||
|
}
|
||||||
buffer.release();
|
buffer.release();
|
||||||
}
|
}
|
||||||
Some(None) => {
|
Some(None) => {
|
||||||
|
|
|
@ -12,7 +12,8 @@ extern crate wayland_server;
|
||||||
mod helpers;
|
mod helpers;
|
||||||
|
|
||||||
use glium::Surface;
|
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 slog::{Drain, Logger};
|
||||||
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
use smithay::backend::graphics::egl::EGLGraphicsBackend;
|
||||||
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
use smithay::backend::input::{self, Event, InputBackend, InputHandler, KeyboardKeyEvent, PointerAxisEvent,
|
||||||
|
@ -226,15 +227,35 @@ 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((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 !
|
// there is actually something to draw !
|
||||||
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(
|
drawer.render_egl(
|
||||||
&mut frame,
|
&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),
|
(w, h),
|
||||||
(x, y),
|
(x, y),
|
||||||
screen_dimensions,
|
screen_dimensions,
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
use super::GraphicsBackend;
|
use super::GraphicsBackend;
|
||||||
#[cfg(feature = "backend_drm")]
|
#[cfg(feature = "backend_drm")]
|
||||||
use gbm::{AsRaw, Device as GbmDevice, Surface as GbmSurface};
|
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 rental::TryNewError;
|
||||||
use slog;
|
use slog;
|
||||||
use std::error;
|
use std::error;
|
||||||
|
@ -27,7 +27,7 @@ use winit::os::unix::WindowExt;
|
||||||
use wayland_server::Display;
|
use wayland_server::Display;
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code, unused_mut)]
|
#[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};
|
use nix::libc::{c_long, c_void, int32_t, uint64_t};
|
||||||
|
|
||||||
pub type khronos_utime_nanoseconds_t = khronos_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 NativePixmapType = *const c_void;
|
||||||
pub type NativeWindowType = *const c_void;
|
pub type NativeWindowType = *const c_void;
|
||||||
|
|
||||||
|
pub mod gl {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/egl_bindings.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
pub mod egl {
|
pub mod egl {
|
||||||
use super::*;
|
use super::*;
|
||||||
use libloading::Library;
|
use libloading::Library;
|
||||||
|
@ -358,12 +362,12 @@ unsafe impl NativeSurface for () {
|
||||||
/// EGL context for rendering
|
/// EGL context for rendering
|
||||||
pub struct EGLContext<'a, T: NativeSurface> {
|
pub struct EGLContext<'a, T: NativeSurface> {
|
||||||
context: ffi::egl::types::EGLContext,
|
context: ffi::egl::types::EGLContext,
|
||||||
display: ffi::egl::types::EGLDisplay,
|
pub(crate) display: ffi::egl::types::EGLDisplay,
|
||||||
config_id: ffi::egl::types::EGLConfig,
|
config_id: ffi::egl::types::EGLConfig,
|
||||||
surface_attributes: Vec<c_int>,
|
surface_attributes: Vec<c_int>,
|
||||||
pixel_format: PixelFormat,
|
pixel_format: PixelFormat,
|
||||||
backend_type: NativeType,
|
backend_type: NativeType,
|
||||||
wl_drm_support: bool,
|
pub(crate) wl_drm_support: bool,
|
||||||
logger: slog::Logger,
|
logger: slog::Logger,
|
||||||
_lifetime: PhantomData<&'a ()>,
|
_lifetime: PhantomData<&'a ()>,
|
||||||
_type: PhantomData<T>,
|
_type: PhantomData<T>,
|
||||||
|
@ -472,6 +476,14 @@ impl<'a, T: NativeSurface> EGLContext<'a, T> {
|
||||||
Err(_) => ptr::null(),
|
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
|
// 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(())
|
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> {}
|
unsafe impl<'a, T: NativeSurface> Send for EGLContext<'a, T> {}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
Loading…
Reference in New Issue