Rework egl image api

This commit is contained in:
Drakulix 2017-12-27 12:20:16 +01:00
parent 5d7e96103d
commit 726991367d
7 changed files with 224 additions and 196 deletions

View File

@ -18,7 +18,6 @@ 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>> {
@ -28,8 +27,8 @@ pub struct EGLContext<B: native::Backend, N: native::NativeDisplay<B>> {
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,
pub(crate) wl_drm_support: bool,
pub(crate) egl_to_texture_support: bool,
logger: slog::Logger,
_backend: PhantomData<B>,
}
@ -474,68 +473,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
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> {}

View File

@ -47,6 +47,11 @@ 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"),
@ -64,6 +69,21 @@ error_chain! {
description("No EGLDisplay is currently bound to this WlDisplay")
}
#[doc = "Index of plane is out of bounds for EGLImages"]
PlaneIndexOutOfBounds {
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")
}
#[doc = "The reason of failure could not be determined"]
Unknown(err_no: u32)
}

View File

@ -19,6 +19,7 @@ pub mod ffi;
pub mod native;
pub mod surface;
pub use self::surface::EGLSurface;
pub mod wayland;
/// Error that can happen when swapping buffers.
#[derive(Debug, Clone, PartialEq)]
@ -166,6 +167,7 @@ pub trait EGLGraphicsBackend: GraphicsBackend {
/// 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

View File

@ -0,0 +1,182 @@
use backend::graphics::egl::{EGLContext, EGLImage, ffi, native};
use backend::graphics::egl::error::*;
use nix::libc::{c_uint};
use std::rc::{Rc, Weak};
use wayland_server::{Display, Resource};
use wayland_server::protocol::wl_buffer::WlBuffer;
#[repr(i32)]
pub enum Format {
RGB = ffi::egl::TEXTURE_RGB as i32,
RGBA = ffi::egl::TEXTURE_RGBA as i32,
External = ffi::egl::TEXTURE_EXTERNAL_WL,
Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
Y_XUXV = ffi::egl::TEXTURE_Y_XUXV_WL,
}
impl Format {
pub fn num_planes(&self) -> usize {
match *self {
Format::RGB | Format::RGBA | Format::External => 1,
Format::Y_UV | Format::Y_XUXV => 2,
Format::Y_U_V => 3,
}
}
}
pub struct EGLImages {
display: Weak<ffi::egl::types::EGLDisplay>,
pub width: u32,
pub height: u32,
pub y_inverted: bool,
pub format: Format,
images: Vec<EGLImage>,
buffer: WlBuffer,
}
impl EGLImages {
pub fn num_planes(&self) -> usize {
self.format.num_planes()
}
pub unsafe fn bind_to_tex(&self, plane: usize, tex_id: c_uint) -> Result<()> {
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)),
}
} else {
bail!(ErrorKind::ContextLost)
}
}
}
impl Drop for EGLImages {
fn drop(&mut self) {
if let Some(display) = self.display.upgrade() {
for image in self.images.drain(..) {
unsafe { ffi::egl::DestroyImageKHR(*display, image); }
}
}
}
}
impl<B: native::Backend, N: native::NativeDisplay<B>> EGLContext<B, N> {
/// 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 fn egl_buffer_contents<T: native::NativeSurface>(&self, buffer: WlBuffer) -> Result<EGLImages> {
if !self.egl_to_texture_support {
bail!(ErrorKind::EglExtensionNotSupported(&["GL_OES_EGL_image"]));
}
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);
}
let 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"),
};
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);
}
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);
}
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 } {
inverted = 1;
}
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..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 =
unsafe { ffi::egl::CreateImageKHR(
*self.display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.ptr() as *mut _,
out.as_ptr(),
) };
if image == ffi::egl::NO_IMAGE_KHR {
bail!(ErrorKind::EGLImageCreationFailed);
} else {
image
}
});
}
Ok(EGLImages {
display: Rc::downgrade(&self.display),
width: width as u32,
height: height as u32,
y_inverted: inverted != 0,
format,
images,
buffer,
})
}
}

View File

@ -87,7 +87,7 @@ impl<
.into_iter()
// Create devices
.flat_map(|path| {
match unsafe { DrmDevice::new(
match DrmDevice::new(
{
match session.open(&path, fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY | fcntl::O_NONBLOCK) {
Ok(fd) => SessionFdDrmDevice(fd),
@ -97,7 +97,7 @@ impl<
}
}
}, logger.clone()
) } {
) {
// Call the handler, which might add it to the runloop
Ok(mut device) => match handler.device_added(&mut evlh.state().as_proxy(), &mut device) {
// fstat them
@ -253,25 +253,23 @@ where
info!(evlh.state().get(token).logger, "Device Added");
if let (Some(path), Some(devnum)) = (event.devnode(), event.devnum()) {
let mut device = {
match unsafe {
DrmDevice::new(
{
let logger = evlh.state().get(token).logger.clone();
match evlh.state().get_mut(token).session.open(
path,
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
| fcntl::O_NONBLOCK,
) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
continue;
}
match DrmDevice::new(
{
let logger = evlh.state().get(token).logger.clone();
match evlh.state().get_mut(token).session.open(
path,
fcntl::O_RDWR | fcntl::O_CLOEXEC | fcntl::O_NOCTTY
| fcntl::O_NONBLOCK,
) {
Ok(fd) => SessionFdDrmDevice(fd),
Err(err) => {
warn!(logger, "Unable to open drm device {:?}, Error: {:?}. Skipping", path, err);
continue;
}
},
evlh.state().get(token).logger.clone(),
)
} {
}
},
evlh.state().get(token).logger.clone(),
) {
Ok(dev) => dev,
Err(err) => {
warn!(

View File

@ -1,110 +0,0 @@
use ::backend::graphics::egl::ffi;
use ::backend::graphics::egl::{EGLContext, NativeSurface};
use ::backend::graphics::egl::EGLImage;
use wayland_server::protocol::wl_buffer::WlBuffer;
use wayland_server::Resource;
/// 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(i32)]
pub enum Format {
RGB = ffi::egl::TEXTURE_RGB as i32,
RGBA = ffi::egl::TEXTURE_RGBA as i32,
External = ffi::egl::TEXTURE_EXTERNAL_WL,
Y_UV = ffi::egl::TEXTURE_Y_UV_WL,
Y_U_V = ffi::egl::TEXTURE_Y_U_V_WL,
Y_XUXV = ffi::egl::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 EGLImages {
pub width: u32,
pub height: u32,
pub y_inverted: bool,
pub format: Format,
images: Vec<EGLImage>,
buffer: WlBuffer,
}
pub fn buffer_contents<T: NativeSurface>(buffer: WlBuffer, context: &EGLContext<T>) -> Result<(Vec<EGLImages>, attributes: Attributes), BufferAccessError>
where
{
let mut format: i32 = 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);
}
let mut width: i32 = 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);
}
let mut height: i32 = 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);
}
let mut inverted: i32 = 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;
}
let mut images = Vec::with_capacity(attributes.format.num_planes() as usize);
for i 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 =
unsafe { ffi::egl::CreateImageKHR(
context.display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.ptr() as *mut _,
out.as_ptr(),
) };
if image == ffi::egl::NO_IMAGE_KHR {
return Err(BufferAccessError::FailedToCreateEGLImage);
} else {
image
}
});
}
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,7 +19,6 @@
//! quickly encounter a panic.
pub mod compositor;
//pub mod drm;
pub mod output;
pub mod seat;
pub mod shm;