use crate::egl_ext as ext;
use crate::glerr;
use ext::{ExtPlatformBaseInstance, KhrImageBaseInstance, OesImageInstance};
use gbm;
use gbm::{AsRaw, FdError};
use gl::types::GLuint;
pub use khronos_egl;
use khronos_egl::Image;
use khronos_egl as egl;
use std::error::Error;
use std::ffi::c_void;
use std::fs;
use std::fs::File;
use std::ops::Deref;
use std::os::fd::{AsRawFd, AsFd, BorrowedFd, OwnedFd};
use std::ptr;
use std::sync::Arc;
use std::sync::atomic;
use std::sync::atomic::AtomicUsize;
#[derive(Clone, Debug)]
pub struct Egl {
display: egl::Display,
context: egl::Context,
config: egl::Config,
device: Arc<gbm::Device<File>>,
}
impl Egl {
unsafe fn make_current(&self) {
egl::Instance::new(egl::Static)
.make_current(
self.display,
None,
None,
Some(self.context),
)
.unwrap()
}
unsafe fn release_current(&self) {
egl::Instance::new(egl::Static)
.make_current(
self.display,
None,
None,
None,
)
.unwrap()
}
}
#[derive(Copy, Clone, Debug)]
pub struct TextureId(GLuint);
impl TextureId {
pub fn as_gl_id(&self) -> GLuint {
self.0
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Dimensions {
pub width: u32,
pub height: u32,
}
#[derive(Debug)]
pub struct Texture {
id: TextureId,
dimensions: Dimensions,
format: gbm::Format,
}
impl Texture {
pub fn id(&self) ->TextureId {
self.id
}
pub fn dimensions(&self) -> Dimensions {
self.dimensions
}
pub fn format(&self) -> gbm::Format {
self.format
}
}
#[derive(Debug)]
pub struct DmabufImage {
texture: TextureId,
image: egl::Image,
dimensions: Dimensions,
format: gbm::Format,
}
#[derive(Debug)]
pub struct DmabufTexture {
texture: glium::Texture2d,
image: egl::Image,
dimensions: Dimensions,
format: gbm::Format,
}
unsafe impl Send for DmabufImage{}
impl DmabufImage {
pub fn get_texture(&self) ->Texture {
Texture {
id: self.texture,
dimensions: self.dimensions,
format: self.format,
}
}
}
pub type DmabufImageRef = DmabufImage;
#[derive(Debug)]
pub struct OwnedDmabufImage {
dmafd: OwnedFd,
image: DmabufImageRef,
}
impl OwnedDmabufImage {
pub fn get_texture(&self) -> &DmabufImageRef {
&self.image
}
pub fn fd(&self) -> BorrowedFd {
self.dmafd.as_fd()
}
}
#[derive(Debug)]
pub struct Tex {
bo: gbm::BufferObject<()>,
image: OwnedDmabufImage,
}
impl Tex {
pub fn new(bo: gbm::BufferObject<()>, image: DmabufImage) -> Result<Self, FdError> {
Ok(Tex {
image: OwnedDmabufImage {
dmafd: bo.fd()?,
image,
},
bo,
})
}
pub fn get_image(&self) -> &OwnedDmabufImage {
&self.image
}
pub fn get_texture(&self) -> &DmabufImageRef {
&self.image.get_texture()
}
}
#[derive(Clone)]
pub struct ContextRef {
pub egl: Egl,
current: Arc<AtomicUsize>,
}
impl ContextRef {
pub fn new() -> Self {
let egl_calls = egl::Instance::new(egl::Static);
let gbm = gbm::Device::new(
fs::OpenOptions::new().read(true).write(true).open("/dev/dri/renderD128").unwrap()
).unwrap();
egl_calls.bind_api(egl::OPENGL_API).unwrap();
let display: egl::Display = unsafe { egl_calls.get_platform_display_ext(
ext::PLATFORM_GBM_MESA as u32,
gbm.as_raw() as _,
&[egl::ATTRIB_NONE],
)}.unwrap();
egl_calls.initialize(display).unwrap();
let configs = {
let mut configs = Vec::with_capacity(32);
egl_calls.choose_config(
display,
&[
egl::BUFFER_SIZE, 32,
egl::DEPTH_SIZE, egl::DONT_CARE,
egl::STENCIL_SIZE, egl::DONT_CARE,
egl::RENDERABLE_TYPE, egl::OPENGL_ES2_BIT,
egl::SURFACE_TYPE, egl::WINDOW_BIT,
egl::NONE,
],
&mut configs,
).unwrap();
configs
};
let config = configs.into_iter().find(|config|
egl_calls.get_config_attrib(
display,
*config,
egl::NATIVE_VISUAL_ID
).unwrap() == gbm::Format::Argb8888 as i32
).unwrap();
let context = egl_calls
.create_context(
display,
config,
None,
&[
egl::CONTEXT_MAJOR_VERSION, 1,
ext::CONTEXT_FLAGS_KHR, ext::CONTEXT_OPENGL_DEBUG_BIT_KHR,
egl::NONE,
],
).unwrap();
Self {
egl: Egl { context, config, device: Arc::new(gbm), display },
current: Arc::new(AtomicUsize::new(0)),
}
}
pub unsafe fn force_make_current(&self) {
if self.current.load(atomic::Ordering::SeqCst) == 0 {
self.egl.make_current();
}
}
fn release_current(&self) {
if self.current.fetch_sub(1, atomic::Ordering::SeqCst) == 0 {
unsafe { self.egl.release_current() };
}
}
pub fn get_proc_address(procname: &str) -> *const c_void {
egl::Instance::new(egl::Static)
.get_proc_address(procname)
.map(|a| a as *const c_void)
.unwrap_or(ptr::null())
}
pub fn get_device(&self) -> Arc<gbm::Device<File>>{
self.egl.device.clone()
}
}
impl EglContext for ContextRef {
fn get_display(&self) -> egl::Display {
self.egl.display
}
fn make_current<'a>(&'a self) -> CurrentContext<'a> {
if self.current.fetch_add(1, atomic::Ordering::SeqCst) == 1 {
unsafe { self.egl.make_current() };
}
CurrentContext(&self)
}
}
pub struct CurrentContext<'a>(&'a ContextRef);
impl<'a> Deref for CurrentContext<'a> {
type Target = ContextRef;
fn deref(&self) -> &Self::Target {
self.0
}
}
impl<'a> Drop for CurrentContext<'a> {
fn drop(&mut self) {
self.0.release_current();
}
}
fn format_bpp(fourcc: gbm::Format) -> Option<usize> {
match fourcc {
gbm::Format::Argb8888 => Some(4),
gbm::Format::R8 => Some(1),
gbm::Format::R16 => Some(2),
gbm::Format::Rg88 => Some(2),
gbm::Format::Gr88 => Some(2),
_ => None,
}
}
pub trait EglContext {
fn make_current<'a>(&'a self) -> CurrentContext<'a>;
fn get_display(&self) -> egl::Display;
}
use thiserror::Error;
#[derive(Debug, Error)]
pub enum DmabufImportError {
#[error("This format is TODO")]
UnsupportedPixelFormat,
#[error("EGL call failed")]
EglError(egl::Error),
#[error("OpenGL call failed")]
GlError(crate::glerr::Error),
}
pub fn import_dmabuf(
egl: &impl EglContext,
fd: BorrowedFd,
dimensions: (u32, u32),
fourcc: gbm::Format,
) -> Result<DmabufImage, Box<dyn Error>> {
let _current = egl.make_current();
Ok(unsafe { import_dmabuf_display(egl.get_display(), fd, dimensions, fourcc) }?)
}
pub unsafe fn import_dmabuf_display(
display: egl::Display,
fd: BorrowedFd,
(width, height): (u32, u32),
fourcc: gbm::Format,
) -> Result<DmabufImage, DmabufImportError> {
let egl_calls = egl::Instance::new(egl::Static);
let bytes_per_pixel = format_bpp(fourcc).ok_or(DmabufImportError::UnsupportedPixelFormat)?;
let image = unsafe {
egl_calls.create_image_khr(
display,
egl::Context::from_ptr(egl::NO_CONTEXT),
ext::LINUX_DMA_BUF_EXT,
egl::ClientBuffer::from_ptr(ptr::null_mut()),
&[
egl::WIDTH as _, width as _,
egl::HEIGHT as _, height as _,
ext::LINUX_DRM_FOURCC_EXT, fourcc as _,
ext::DMA_BUF_PLANE0_FD_EXT, fd.as_raw_fd() as _,
ext::DMA_BUF_PLANE0_OFFSET_EXT, 0,
ext::DMA_BUF_PLANE0_PITCH_EXT, (width as egl::Int) * (bytes_per_pixel as egl::Int),
egl::NONE,
],
)
}.map_err(DmabufImportError::EglError)?;
gl::load_with(|s| egl_calls.get_proc_address(s).unwrap() as _);
let texture = {
let mut texture = 0;
glerr::check(unsafe {
gl::GenTextures(1, &mut texture);
texture
})
}.map_err(DmabufImportError::GlError)?;
Ok(DmabufImage {
texture: TextureId(texture),
image,
dimensions: Dimensions { width, height },
format: fourcc,
})
}
pub fn attach_image(_egl: CurrentContext, buf: DmabufImage) -> Result<DmabufImage, DmabufImportError> {
unsafe { attach_image_current(buf) }
}
pub unsafe fn attach_image_current(buf: DmabufImage) -> Result<DmabufImage, DmabufImportError> {
let egl_calls = egl::Instance::new(egl::Static);
gl::load_with(|s| egl_calls.get_proc_address(s).unwrap() as _);
glerr::check(unsafe {
gl::BindTexture(gl::TEXTURE_2D, buf.texture.0)
}).map_err(DmabufImportError::GlError)?;
unsafe {
egl_calls.image_target_texture_2d_oes(gl::TEXTURE_2D, buf.image)
}.map_err(DmabufImportError::EglError)?;
Ok(buf)
}