use std::os::fd::IntoRawFd;
use gbm::BufferObject;
use r_egl_wayland::{EGL_INSTALCE, WayEglTrait, r_egl as egl};
use wayland_client::protocol::wl_display::WlDisplay;
use crate::error::{Error, Result};
use crate::screencopy::DMAFrameFormat;
pub type EglDisplay = egl::Display;
pub struct EGLImageGuard {
pub image: egl::Image,
pub(crate) egl_display: egl::Display,
}
impl Drop for EGLImageGuard {
fn drop(&mut self) {
EGL_INSTALCE
.destroy_image(self.egl_display, self.image)
.unwrap_or_else(|e| {
tracing::error!("EGLimage destruction had error: {e}");
});
}
}
pub fn get_egl_display_wl(display: &WlDisplay) -> Result<egl::Display> {
EGL_INSTALCE.get_display_wl(display).map_err(Error::from)
}
pub fn initialize_egl(display: egl::Display) -> Result<()> {
EGL_INSTALCE
.initialize(display)
.map(|_| ())
.map_err(Error::from)
}
pub fn create_egl_image_from_dmabuf(
egl_display: egl::Display,
bo: &BufferObject<()>,
frame_format: &DMAFrameFormat,
) -> Result<EGLImageGuard> {
type Attrib = egl::Attrib;
let modifier: u64 = bo.modifier().into();
let image_attribs = [
egl::WIDTH as Attrib,
frame_format.size.width as Attrib,
egl::HEIGHT as Attrib,
frame_format.size.height as Attrib,
egl::LINUX_DRM_FOURCC_EXT as Attrib,
bo.format() as Attrib,
egl::DMA_BUF_PLANE0_FD_EXT as Attrib,
bo.fd_for_plane(0)?.into_raw_fd() as Attrib,
egl::DMA_BUF_PLANE0_OFFSET_EXT as Attrib,
bo.offset(0) as Attrib,
egl::DMA_BUF_PLANE0_PITCH_EXT as Attrib,
bo.stride_for_plane(0) as Attrib,
egl::DMA_BUF_PLANE0_MODIFIER_LO_EXT as Attrib,
(modifier as u32) as Attrib,
egl::DMA_BUF_PLANE0_MODIFIER_HI_EXT as Attrib,
(modifier >> 32) as Attrib,
egl::ATTRIB_NONE as Attrib,
];
tracing::debug!(
"Calling eglCreateImage with attributes: {:#?}",
image_attribs
);
unsafe {
let image = EGL_INSTALCE
.create_image(
egl_display,
egl::Context::from_ptr(egl::NO_CONTEXT),
egl::LINUX_DMA_BUF_EXT as u32,
egl::ClientBuffer::from_ptr(std::ptr::null_mut()),
&image_attribs,
)
.map_err(|e| {
tracing::error!("eglCreateImage call failed with error {e}");
Error::from(e)
})?;
Ok(EGLImageGuard { image, egl_display })
}
}
pub fn bind_egl_image_to_gl_texture(guard: &EGLImageGuard) -> Result<()> {
let gl_egl_image_texture_target_2d_oes = unsafe {
let f = match EGL_INSTALCE.get_proc_address("glEGLImageTargetTexture2DOES") {
Some(f) => {
tracing::debug!("glEGLImageTargetTexture2DOES found at address {:#?}", f);
f
}
None => {
tracing::error!("glEGLImageTargetTexture2DOES not found");
return Err(Error::EGLImageToTexProcNotFoundError);
}
};
std::mem::transmute::<
extern "system" fn(),
unsafe extern "system" fn(gl::types::GLenum, gl::types::GLeglImageOES) -> (),
>(f)
};
unsafe {
gl_egl_image_texture_target_2d_oes(gl::TEXTURE_2D, guard.image.as_ptr());
tracing::trace!("glEGLImageTargetTexture2DOES called");
}
Ok(())
}
pub fn create_egl_image_and_bind_to_gl_texture(
egl_display: egl::Display,
bo: &BufferObject<()>,
frame_format: &DMAFrameFormat,
) -> Result<()> {
let guard = create_egl_image_from_dmabuf(egl_display, bo, frame_format)?;
bind_egl_image_to_gl_texture(&guard)?;
drop(guard);
Ok(())
}