use std::{
collections::HashSet,
ffi::{c_int, CStr},
hash::{Hash, Hasher},
mem::MaybeUninit,
ops::Deref,
os::unix::io::{AsRawFd, FromRawFd, OwnedFd},
sync::{Arc, LazyLock, Mutex, Weak},
};
use indexmap::IndexSet;
use libc::c_void;
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
use wayland_server::{protocol::wl_buffer::WlBuffer, DisplayHandle, Resource};
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
use wayland_sys::server::wl_display;
use crate::backend::allocator::format::FormatSet;
#[cfg(feature = "backend_drm")]
use crate::backend::egl::EGLDevice;
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use crate::backend::egl::{BufferAccessError, EGLBuffer, Format};
use crate::{
backend::{
allocator::{dmabuf::Dmabuf, Buffer as _, Format as DrmFormat, Fourcc, Modifier},
egl::{
context::{GlAttributes, PixelFormatRequirements},
ffi,
ffi::egl::types::EGLImage,
native::EGLNativeDisplay,
wrap_egl_call_bool, wrap_egl_call_ptr, EGLError, Error,
},
},
utils::{Buffer as BufferCoords, Size},
};
use tracing::{debug, error, info, info_span, instrument, trace, warn};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
pub(crate) static BUFFER_READER: Mutex<Option<WeakBufferReader>> = Mutex::new(None);
static DISPLAYS: LazyLock<Mutex<HashSet<WeakEGLDisplayHandle>>> =
LazyLock::new(|| Mutex::new(HashSet::new()));
#[derive(Debug)]
pub struct EGLDisplayHandle {
pub handle: ffi::egl::types::EGLDisplay,
should_terminate: bool,
_native: Box<dyn std::any::Any + 'static>,
}
unsafe impl Send for EGLDisplayHandle {}
unsafe impl Sync for EGLDisplayHandle {}
#[derive(Clone)]
struct WeakEGLDisplayHandle {
handle: Weak<EGLDisplayHandle>,
ptr: ffi::egl::types::EGLDisplay,
}
unsafe impl Send for WeakEGLDisplayHandle {}
unsafe impl Sync for WeakEGLDisplayHandle {}
impl From<Arc<EGLDisplayHandle>> for WeakEGLDisplayHandle {
#[inline]
fn from(other: Arc<EGLDisplayHandle>) -> Self {
WeakEGLDisplayHandle {
handle: Arc::downgrade(&other),
ptr: other.handle,
}
}
}
impl Hash for WeakEGLDisplayHandle {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.ptr.hash(state);
}
}
impl PartialEq for WeakEGLDisplayHandle {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.ptr == other.ptr
}
}
impl Eq for WeakEGLDisplayHandle {}
impl Deref for EGLDisplayHandle {
type Target = ffi::egl::types::EGLDisplay;
#[inline]
fn deref(&self) -> &Self::Target {
&self.handle
}
}
impl Drop for EGLDisplayHandle {
#[inline]
fn drop(&mut self) {
if self.should_terminate {
unsafe {
ffi::egl::Terminate(self.handle);
}
}
}
}
#[derive(Debug, Clone)]
pub struct EGLDisplay {
display: Arc<EGLDisplayHandle>,
egl_version: (i32, i32),
extensions: Vec<String>,
dmabuf_import_formats: FormatSet,
dmabuf_render_formats: FormatSet,
surface_type: ffi::EGLint,
pub(super) has_fences: bool,
pub(super) supports_native_fences: bool,
pub(super) span: tracing::Span,
}
unsafe fn select_platform_display<N: EGLNativeDisplay + 'static>(
native: &N,
dp_extensions: &[String],
) -> Result<(*const c_void, &'static str), Error> {
for platform in native.supported_platforms() {
debug!("Trying EGL platform: {}", platform.platform_name);
let missing_extensions = platform
.required_extensions
.iter()
.filter(|ext| !dp_extensions.iter().any(|x| x == *ext))
.collect::<Vec<_>>();
if !missing_extensions.is_empty() {
info!(
"Skipping EGL platform because one or more required extensions are not supported. Missing extensions: {:?}", missing_extensions
);
continue;
}
let display = wrap_egl_call_ptr(|| {
ffi::egl::GetPlatformDisplayEXT(
platform.platform,
platform.native_display,
platform.attrib_list.as_ptr(),
)
})
.map_err(Error::DisplayCreationError);
let display = match display {
Ok(display) => {
if display == ffi::egl::NO_DISPLAY {
info!("Skipping platform because the display is not supported");
continue;
}
display
}
Err(err) => {
info!(
"Skipping platform because of an display creation error: {:?}",
err
);
continue;
}
};
info!("Successfully selected EGL platform: {}", platform.platform_name);
return Ok((display, platform.platform_name));
}
error!("Unable to find suitable EGL platform");
Err(Error::DisplayNotSupported)
}
impl EGLDisplay {
pub unsafe fn new<N>(native: N) -> Result<EGLDisplay, Error>
where
N: EGLNativeDisplay + 'static,
{
let span = info_span!(
"egl",
platform = tracing::field::Empty,
native = tracing::field::Empty,
version = tracing::field::Empty,
);
if let Some(value) = native.identifier() {
span.record("native", value);
}
let dp_extensions = ffi::make_sure_egl_is_loaded()?;
debug!("Supported EGL client extensions: {:?}", dp_extensions);
let surface_type = native.surface_type();
let (display, platform) = unsafe { select_platform_display(&native, &dp_extensions)? };
span.record("platform", platform);
let display = {
let new_display = Arc::new(EGLDisplayHandle {
handle: display,
should_terminate: true,
_native: Box::new(native),
});
let weak_disp = WeakEGLDisplayHandle::from(new_display.clone());
let mut displays = DISPLAYS.lock().unwrap();
displays.retain(|handle| handle.handle.strong_count() != 0);
if displays.insert(weak_disp.clone()) {
new_display
} else {
Arc::try_unwrap(new_display).unwrap().should_terminate = false;
displays.get(&weak_disp).unwrap().handle.upgrade().unwrap()
}
};
let egl_version = unsafe {
let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
let mut minor: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
wrap_egl_call_bool(|| {
ffi::egl::Initialize(display.handle, major.as_mut_ptr(), minor.as_mut_ptr())
})
.map_err(Error::InitFailed)?;
let major = major.assume_init();
let minor = minor.assume_init();
info!("EGL Initialized");
info!("EGL Version: {:?}", (major, minor));
(major, minor)
};
span.record("version", tracing::field::debug(egl_version));
let extensions = EGLDisplay::get_extensions(egl_version, display.handle)?;
info!("Supported EGL display extensions: {:?}", extensions);
let (dmabuf_import_formats, dmabuf_render_formats) =
get_dmabuf_formats(&display.handle, &extensions).map_err(Error::DisplayCreationError)?;
if egl_version <= (1, 2) {
return Err(Error::OpenGlesNotSupported(None));
}
wrap_egl_call_bool(|| unsafe { ffi::egl::BindAPI(ffi::egl::OPENGL_ES_API) })
.map_err(|source| Error::OpenGlesNotSupported(Some(source)))?;
let has_fences = extensions.iter().any(|s| s == "EGL_KHR_fence_sync");
let supports_native_fences =
has_fences && extensions.iter().any(|s| s == "EGL_ANDROID_native_fence_sync");
Ok(EGLDisplay {
display,
surface_type,
egl_version,
extensions,
dmabuf_import_formats,
dmabuf_render_formats,
has_fences,
supports_native_fences,
span,
})
}
pub unsafe fn from_raw(display: *const c_void, config_id: *const c_void) -> Result<EGLDisplay, Error> {
let span = info_span!("egl", platform = "unknown/raw", version = tracing::field::Empty);
assert!(!display.is_null(), "EGLDisplay pointer is null");
assert!(!config_id.is_null(), "EGL configuration id pointer is null");
let dp_extensions = ffi::make_sure_egl_is_loaded()?;
debug!("Supported EGL client extensions: {:?}", dp_extensions);
let egl_version = {
let p = CStr::from_ptr(
wrap_egl_call_ptr(|| ffi::egl::QueryString(display, ffi::egl::VERSION as i32))
.map_err(|_| Error::DisplayQueryResultInvalid)?,
);
let version_string = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
let mut version_iterator = version_string
.split(' ')
.next()
.ok_or(Error::DisplayQueryResultInvalid)?
.split('.');
let major = version_iterator
.next()
.and_then(|v| v.parse::<i32>().ok())
.ok_or(Error::DisplayQueryResultInvalid)?;
let minor = version_iterator
.next()
.and_then(|v| v.parse::<i32>().ok())
.ok_or(Error::DisplayQueryResultInvalid)?;
info!("EGL Version: {:?}", (major, minor));
(major, minor)
};
span.record("version", tracing::field::debug(egl_version));
let extensions = EGLDisplay::get_extensions(egl_version, display)?;
info!("Supported EGL display extensions: {:?}", extensions);
let (dmabuf_import_formats, dmabuf_render_formats) =
get_dmabuf_formats(&display, &extensions).map_err(Error::DisplayCreationError)?;
let egl_api = ffi::egl::QueryAPI();
if egl_api != ffi::egl::OPENGL_ES_API {
return Err(Error::OpenGlesNotSupported(None));
}
let surface_type = {
let mut surface_type: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
wrap_egl_call_bool(|| {
ffi::egl::GetConfigAttrib(
display,
config_id,
ffi::egl::SURFACE_TYPE as i32,
surface_type.as_mut_ptr(),
)
})
.map_err(|_| Error::OpenGlesNotSupported(None))?;
surface_type.assume_init()
};
let has_fences = extensions.iter().any(|s| s == "EGL_KHR_fence_sync");
let supports_native_fences =
has_fences && extensions.iter().any(|s| s == "EGL_ANDROID_native_fence_sync");
Ok(EGLDisplay {
display: Arc::new(EGLDisplayHandle {
handle: display,
should_terminate: false,
_native: Box::new(()),
}),
surface_type,
egl_version,
extensions,
dmabuf_import_formats,
dmabuf_render_formats,
has_fences,
supports_native_fences,
span,
})
}
fn get_extensions(egl_version: (i32, i32), display: *const c_void) -> Result<Vec<String>, Error> {
if egl_version >= (1, 2) {
let p = unsafe {
CStr::from_ptr(
wrap_egl_call_ptr(|| ffi::egl::QueryString(display, ffi::egl::EXTENSIONS as i32))
.map_err(Error::InitFailed)?,
)
};
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
Ok(list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>())
} else {
Ok(vec![])
}
}
pub fn choose_config(
&self,
attributes: GlAttributes,
reqs: PixelFormatRequirements,
) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {
let descriptor = {
let mut out: Vec<c_int> = Vec::with_capacity(37);
if self.egl_version >= (1, 2) {
trace!("Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
out.push(ffi::egl::COLOR_BUFFER_TYPE as c_int);
out.push(ffi::egl::RGB_BUFFER as c_int);
}
trace!("Setting SURFACE_TYPE to {}", self.surface_type);
out.push(ffi::egl::SURFACE_TYPE as c_int);
out.push(self.surface_type);
match attributes.version {
(3, _) => {
if self.egl_version < (1, 3) {
error!("OpenglES 3.* is not supported on EGL Versions lower then 1.3");
return Err(Error::NoAvailablePixelFormat);
}
trace!("Setting RENDERABLE_TYPE to OPENGL_ES3");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
trace!("Setting CONFORMANT to OPENGL_ES3");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES3_BIT as c_int);
}
(2, _) => {
if self.egl_version < (1, 3) {
error!("OpenglES 2.* is not supported on EGL Versions lower then 1.3");
return Err(Error::NoAvailablePixelFormat);
}
trace!("Setting RENDERABLE_TYPE to OPENGL_ES2");
out.push(ffi::egl::RENDERABLE_TYPE as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
trace!("Setting CONFORMANT to OPENGL_ES2");
out.push(ffi::egl::CONFORMANT as c_int);
out.push(ffi::egl::OPENGL_ES2_BIT as c_int);
}
ver => {
return Err(Error::OpenGlVersionNotSupported(ver));
}
};
reqs.create_attributes(&mut out);
out.push(ffi::egl::NONE as c_int);
out
};
let mut num_configs = 0;
wrap_egl_call_bool(|| unsafe {
ffi::egl::ChooseConfig(
**self.display,
descriptor.as_ptr(),
std::ptr::null_mut(),
0,
&mut num_configs,
)
})
.map_err(Error::ConfigFailed)?;
if num_configs == 0 {
return Err(Error::NoAvailablePixelFormat);
}
let mut config_ids: Vec<ffi::egl::types::EGLConfig> = Vec::with_capacity(num_configs as usize);
wrap_egl_call_bool(|| unsafe {
ffi::egl::ChooseConfig(
**self.display,
descriptor.as_ptr(),
config_ids.as_mut_ptr(),
num_configs,
&mut num_configs,
)
})
.map_err(Error::ConfigFailed)?;
unsafe {
config_ids.set_len(num_configs as usize);
}
if config_ids.is_empty() {
return Err(Error::NoAvailablePixelFormat);
}
let desired_swap_interval = i32::from(attributes.vsync);
let config_id = config_ids
.iter()
.copied()
.map(|config| unsafe {
let mut min_swap_interval = 0;
wrap_egl_call_bool(|| {
ffi::egl::GetConfigAttrib(
**self.display,
config,
ffi::egl::MIN_SWAP_INTERVAL as ffi::egl::types::EGLint,
&mut min_swap_interval,
)
})?;
if desired_swap_interval < min_swap_interval {
return Ok(None);
}
let mut max_swap_interval = 0;
wrap_egl_call_bool(|| {
ffi::egl::GetConfigAttrib(
**self.display,
config,
ffi::egl::MAX_SWAP_INTERVAL as ffi::egl::types::EGLint,
&mut max_swap_interval,
)
})?;
if desired_swap_interval > max_swap_interval {
return Ok(None);
}
Ok(Some(config))
})
.collect::<Result<Vec<Option<ffi::egl::types::EGLConfig>>, EGLError>>()
.map_err(Error::ConfigFailed)?
.into_iter()
.flatten()
.next()
.unwrap_or_else(|| config_ids[0]);
let desc = unsafe { self.get_pixel_format(config_id)? };
info!("Selected color format: {:?}", desc);
Ok((desc, config_id))
}
pub(super) unsafe fn get_pixel_format(&self, config_id: *const c_void) -> Result<PixelFormat, Error> {
macro_rules! attrib {
($display:expr, $config:expr, $attr:expr) => {{
let mut value = MaybeUninit::uninit();
wrap_egl_call_bool(|| {
ffi::egl::GetConfigAttrib(
**$display,
$config,
$attr as ffi::egl::types::EGLint,
value.as_mut_ptr(),
)
})
.map_err(Error::ConfigFailed)?;
value.assume_init()
}};
}
Ok(PixelFormat {
hardware_accelerated: attrib!(self.display, config_id, ffi::egl::CONFIG_CAVEAT)
!= ffi::egl::SLOW_CONFIG as i32,
color_bits: attrib!(self.display, config_id, ffi::egl::RED_SIZE) as u8
+ attrib!(self.display, config_id, ffi::egl::BLUE_SIZE) as u8
+ attrib!(self.display, config_id, ffi::egl::GREEN_SIZE) as u8,
alpha_bits: attrib!(self.display, config_id, ffi::egl::ALPHA_SIZE) as u8,
depth_bits: attrib!(self.display, config_id, ffi::egl::DEPTH_SIZE) as u8,
stencil_bits: attrib!(self.display, config_id, ffi::egl::STENCIL_SIZE) as u8,
stereoscopy: false,
multisampling: match attrib!(self.display, config_id, ffi::egl::SAMPLES) {
0 | 1 => None,
a => Some(a as u16),
},
srgb: false, })
}
pub fn get_display_handle(&self) -> Arc<EGLDisplayHandle> {
self.display.clone()
}
pub fn get_egl_version(&self) -> (i32, i32) {
self.egl_version
}
pub fn extensions(&self) -> &[String] {
&self.extensions[..]
}
pub fn dmabuf_render_formats(&self) -> &FormatSet {
&self.dmabuf_render_formats
}
pub fn dmabuf_texture_formats(&self) -> &FormatSet {
&self.dmabuf_import_formats
}
pub fn supports_damage(&self) -> bool {
self.supports_damage_impl().supported()
}
pub(super) fn supports_damage_impl(&self) -> DamageSupport {
if self.extensions.iter().any(|ext| ext == "EGL_EXT_buffer_age") {
if self
.extensions
.iter()
.any(|ext| ext == "EGL_KHR_swap_buffers_with_damage")
{
return DamageSupport::KHR;
} else if self
.extensions
.iter()
.any(|ext| ext == "EGL_EXT_swap_buffers_with_damage")
{
return DamageSupport::EXT;
}
}
DamageSupport::No
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[instrument(level = "trace", skip(self), parent = &self.span, err)]
#[profiling::function]
pub fn create_dmabuf_from_image(
&self,
image: EGLImage,
size: Size<i32, BufferCoords>,
y_inverted: bool,
) -> Result<Dmabuf, Error> {
use crate::backend::allocator::dmabuf::DmabufFlags;
if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")
&& !self
.extensions
.iter()
.any(|s| s == "EGL_MESA_image_dma_buf_export")
{
return Err(Error::EglExtensionNotSupported(&[
"EGL_KHR_image_base",
"EGL_MESA_image_dma_buf_export",
]));
}
let mut format: c_int = 0;
let mut num_planes: c_int = 0;
let mut modifier: ffi::egl::types::EGLuint64KHR = 0;
wrap_egl_call_bool(|| unsafe {
ffi::egl::ExportDMABUFImageQueryMESA(
**self.display,
image,
&mut format as *mut _,
&mut num_planes as *mut _,
&mut modifier as *mut _,
)
})
.map_err(Error::DmabufExportFailed)?;
let mut fds: Vec<c_int> = Vec::with_capacity(num_planes as usize);
let mut strides: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);
let mut offsets: Vec<ffi::egl::types::EGLint> = Vec::with_capacity(num_planes as usize);
unsafe {
wrap_egl_call_bool(|| {
ffi::egl::ExportDMABUFImageMESA(
**self.display,
image,
fds.as_mut_ptr(),
strides.as_mut_ptr(),
offsets.as_mut_ptr(),
)
})
.map_err(Error::DmabufExportFailed)?;
fds.set_len(num_planes as usize);
strides.set_len(num_planes as usize);
offsets.set_len(num_planes as usize);
}
let mut dma = Dmabuf::builder(
size,
Fourcc::try_from(format as u32).expect("Unknown format"),
Modifier::from(modifier),
if y_inverted {
DmabufFlags::Y_INVERT
} else {
DmabufFlags::empty()
},
);
for i in 0..num_planes {
dma.add_plane(
unsafe { OwnedFd::from_raw_fd(fds[i as usize]) },
i as u32,
offsets[i as usize] as u32,
strides[i as usize] as u32,
);
}
#[cfg(feature = "backend_drm")]
if let Some(node) = EGLDevice::device_for_display(self)
.ok()
.and_then(|device| device.try_get_render_node().ok().flatten())
{
dma.set_node(node);
}
dma.build().ok_or(Error::DmabufExportFailed(EGLError::BadAlloc))
}
#[instrument(level = "trace", skip(self), parent = &self.span, err)]
#[profiling::function]
pub fn create_image_from_dmabuf(&self, dmabuf: &Dmabuf) -> Result<EGLImage, Error> {
if !self.extensions.iter().any(|s| s == "EGL_KHR_image_base")
&& !self
.extensions
.iter()
.any(|s| s == "EGL_EXT_image_dma_buf_import")
{
return Err(Error::EglExtensionNotSupported(&[
"EGL_KHR_image_base",
"EGL_EXT_image_dma_buf_import",
]));
}
if dmabuf.has_modifier()
&& !self
.extensions
.iter()
.any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")
{
return Err(Error::EglExtensionNotSupported(&[
"EGL_EXT_image_dma_buf_import_modifiers",
]));
};
let mut out: Vec<c_int> = Vec::with_capacity(50);
out.extend([
ffi::egl::WIDTH as i32,
dmabuf.width() as i32,
ffi::egl::HEIGHT as i32,
dmabuf.height() as i32,
ffi::egl::LINUX_DRM_FOURCC_EXT as i32,
dmabuf.format().code as u32 as i32,
]);
let names = [
[
ffi::egl::DMA_BUF_PLANE0_FD_EXT,
ffi::egl::DMA_BUF_PLANE0_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE0_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE0_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE0_MODIFIER_HI_EXT,
],
[
ffi::egl::DMA_BUF_PLANE1_FD_EXT,
ffi::egl::DMA_BUF_PLANE1_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE1_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE1_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE1_MODIFIER_HI_EXT,
],
[
ffi::egl::DMA_BUF_PLANE2_FD_EXT,
ffi::egl::DMA_BUF_PLANE2_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE2_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE2_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE2_MODIFIER_HI_EXT,
],
[
ffi::egl::DMA_BUF_PLANE3_FD_EXT,
ffi::egl::DMA_BUF_PLANE3_OFFSET_EXT,
ffi::egl::DMA_BUF_PLANE3_PITCH_EXT,
ffi::egl::DMA_BUF_PLANE3_MODIFIER_LO_EXT,
ffi::egl::DMA_BUF_PLANE3_MODIFIER_HI_EXT,
],
];
for (i, ((fd, offset), stride)) in dmabuf
.handles()
.zip(dmabuf.offsets())
.zip(dmabuf.strides())
.enumerate()
{
out.extend([
names[i][0] as i32,
fd.as_raw_fd(),
names[i][1] as i32,
offset as i32,
names[i][2] as i32,
stride as i32,
]);
if dmabuf.has_modifier() {
out.extend([
names[i][3] as i32,
(Into::<u64>::into(dmabuf.format().modifier) & 0xFFFFFFFF) as i32,
names[i][4] as i32,
(Into::<u64>::into(dmabuf.format().modifier) >> 32) as i32,
])
}
}
out.push(ffi::egl::NONE as i32);
unsafe {
let image = ffi::egl::CreateImageKHR(
**self.display,
ffi::egl::NO_CONTEXT,
ffi::egl::LINUX_DMA_BUF_EXT,
std::ptr::null(),
out.as_ptr(),
);
if image == ffi::egl::NO_IMAGE_KHR {
Err(Error::EGLImageCreationFailed)
} else {
Ok(image)
}
}
}
#[cfg(all(feature = "use_system_lib", feature = "wayland_frontend"))]
pub fn bind_wl_display(&self, display: &DisplayHandle) -> Result<EGLBufferReader, Error> {
let display_ptr = display.backend_handle().display_ptr();
if !self.extensions.iter().any(|s| s == "EGL_WL_bind_wayland_display") {
return Err(Error::EglExtensionNotSupported(&["EGL_WL_bind_wayland_display"]));
}
wrap_egl_call_bool(|| unsafe {
ffi::egl::BindWaylandDisplayWL(**self.display, display_ptr as *mut _)
})
.map_err(Error::OtherEGLDisplayAlreadyBound)?;
let reader = EGLBufferReader::new(self.display.clone(), display_ptr);
let mut global = BUFFER_READER.lock().unwrap();
if global.as_ref().and_then(|x| x.upgrade()).is_some() {
warn!("Double bind_wl_display, smithay does not support this, please report");
}
*global = Some(WeakBufferReader {
display: Arc::downgrade(&self.display),
});
Ok(reader)
}
}
fn get_dmabuf_formats(
display: &ffi::egl::types::EGLDisplay,
extensions: &[String],
) -> Result<(FormatSet, FormatSet), EGLError> {
if !extensions.iter().any(|s| s == "EGL_EXT_image_dma_buf_import") {
warn!("Dmabuf import extension not available");
return Ok((FormatSet::default(), FormatSet::default()));
}
let formats = {
if !extensions
.iter()
.any(|s| s == "EGL_EXT_image_dma_buf_import_modifiers")
{
vec![Fourcc::Argb8888, Fourcc::Xrgb8888]
} else {
let mut num = 0i32;
wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryDmaBufFormatsEXT(*display, 0, std::ptr::null_mut(), &mut num as *mut _)
})?;
if num == 0 {
return Ok((FormatSet::default(), FormatSet::default()));
}
let mut formats: Vec<u32> = Vec::with_capacity(num as usize);
wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryDmaBufFormatsEXT(
*display,
num,
formats.as_mut_ptr() as *mut _,
&mut num as *mut _,
)
})?;
unsafe {
formats.set_len(num as usize);
}
formats
.into_iter()
.flat_map(|x| Fourcc::try_from(x).ok())
.collect::<Vec<_>>()
}
};
let mut texture_formats = IndexSet::new();
let mut render_formats = IndexSet::new();
for fourcc in formats {
let mut num = 0i32;
match wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryDmaBufModifiersEXT(
*display,
fourcc as i32,
0,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut num as *mut _,
)
}) {
Ok(_) => {}
Err(EGLError::BadParameter) => {
debug!(
"eglQueryDmaBufModifiersEXT returned BadParameter for {:?}",
fourcc
);
num = 0;
}
Err(x) => {
return Err(x);
}
};
if num != 0 {
let mut mods: Vec<u64> = Vec::with_capacity(num as usize);
let mut external: Vec<ffi::egl::types::EGLBoolean> = Vec::with_capacity(num as usize);
wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryDmaBufModifiersEXT(
*display,
fourcc as i32,
num,
mods.as_mut_ptr(),
external.as_mut_ptr(),
&mut num as *mut _,
)
})?;
unsafe {
mods.set_len(num as usize);
external.set_len(num as usize);
}
for (modifier, external_only) in mods.into_iter().zip(external.into_iter()) {
let format = DrmFormat {
code: fourcc,
modifier: Modifier::from(modifier),
};
texture_formats.insert(format);
if external_only == 0 {
render_formats.insert(format);
}
}
}
texture_formats.insert(DrmFormat {
code: fourcc,
modifier: Modifier::Invalid,
});
render_formats.insert(DrmFormat {
code: fourcc,
modifier: Modifier::Invalid,
});
}
trace!("Supported dmabuf import formats: {:?}", texture_formats);
trace!("Supported dmabuf render formats: {:?}", render_formats);
Ok((
FormatSet::from_formats(texture_formats),
FormatSet::from_formats(render_formats),
))
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
#[derive(Debug, Clone)]
pub struct EGLBufferReader {
display: Arc<EGLDisplayHandle>,
wayland: Option<Arc<*mut wl_display>>,
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
pub(crate) struct WeakBufferReader {
display: Weak<EGLDisplayHandle>,
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
impl WeakBufferReader {
pub fn upgrade(&self) -> Option<EGLBufferReader> {
Some(EGLBufferReader {
display: self.display.upgrade()?,
wayland: None,
})
}
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
unsafe impl Send for EGLBufferReader {}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
impl EGLBufferReader {
fn new(display: Arc<EGLDisplayHandle>, wayland: *mut wl_display) -> Self {
#[allow(clippy::arc_with_non_send_sync)]
Self {
display,
wayland: Some(Arc::new(wayland)),
}
}
#[profiling::function]
pub fn egl_buffer_contents(
&self,
buffer: &WlBuffer,
) -> ::std::result::Result<EGLBuffer, BufferAccessError> {
if !buffer.is_alive() {
return Err(BufferAccessError::Destroyed);
}
let mut format: i32 = 0;
let query = wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::EGL_TEXTURE_FORMAT,
&mut format,
)
})
.map_err(BufferAccessError::NotManaged)?;
if query == ffi::egl::FALSE {
return Err(BufferAccessError::NotManaged(EGLError::BadParameter));
}
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 => {
return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_UV))
}
ffi::egl::TEXTURE_Y_U_V_WL => {
return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_U_V))
}
ffi::egl::TEXTURE_Y_XUXV_WL => {
return Err(BufferAccessError::UnsupportedMultiPlanarFormat(Format::Y_XUXV))
}
x => panic!("EGL returned invalid texture type: {}", x),
};
let mut width: i32 = 0;
wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::WIDTH as i32,
&mut width,
)
})
.map_err(BufferAccessError::NotManaged)?;
let mut height: i32 = 0;
wrap_egl_call_bool(|| unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::HEIGHT as i32,
&mut height,
)
})
.map_err(BufferAccessError::NotManaged)?;
let y_inverted = {
let mut inverted: i32 = 0;
match (
unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::WAYLAND_Y_INVERTED_WL,
&mut inverted,
)
},
EGLError::from_last_call(),
) {
(ffi::egl::TRUE, None) => inverted != 0,
(ffi::egl::FALSE, None) => true,
(_, Some(e)) => return Err(BufferAccessError::NotManaged(e)),
_ => unreachable!(),
}
};
let mut images = Vec::with_capacity(format.num_planes());
for i in 0..format.num_planes() {
let out = [ffi::egl::WAYLAND_PLANE_WL as i32, i as i32, ffi::egl::NONE as i32];
images.push({
wrap_egl_call_ptr(|| unsafe {
ffi::egl::CreateImageKHR(
**self.display,
ffi::egl::NO_CONTEXT,
ffi::egl::WAYLAND_BUFFER_WL,
buffer.id().as_ptr() as *mut _,
out.as_ptr(),
)
})
.map_err(BufferAccessError::EGLImageCreationFailed)?
});
}
Ok(EGLBuffer {
display: self.display.clone(),
size: (width, height).into(),
y_inverted: !y_inverted,
format,
images,
})
}
#[profiling::function]
pub fn egl_buffer_dimensions(
&self,
buffer: &WlBuffer,
) -> Option<crate::utils::Size<i32, crate::utils::Buffer>> {
if !buffer.is_alive() {
return None;
}
let mut width: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::WIDTH as _,
&mut width,
) == 0
} {
return None;
}
let mut height: i32 = 0;
if unsafe {
ffi::egl::QueryWaylandBufferWL(
**self.display,
buffer.id().as_ptr() as _,
ffi::egl::HEIGHT as _,
&mut height,
) == 0
} {
return None;
}
Some((width, height).into())
}
pub fn egl_buffer_has_alpha(format: Format) -> bool {
!matches!(
format,
Format::RGB | Format::Y_UV | Format::Y_U_V | Format::Y_XUXV
)
}
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
impl Drop for EGLBufferReader {
fn drop(&mut self) {
if let Some(wayland) = self.wayland.take().and_then(|x| Arc::try_unwrap(x).ok()) {
if !wayland.is_null() {
unsafe {
ffi::egl::UnbindWaylandDisplayWL(**self.display, wayland as _);
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PixelFormat {
pub hardware_accelerated: bool,
pub color_bits: u8,
pub alpha_bits: u8,
pub depth_bits: u8,
pub stencil_bits: u8,
pub stereoscopy: bool,
pub multisampling: Option<u16>,
pub srgb: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum DamageSupport {
KHR,
EXT,
No,
}
impl DamageSupport {
pub fn supported(&self) -> bool {
self != &DamageSupport::No
}
}