use tracing::warn;
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use wayland_server::protocol::wl_buffer;
use crate::backend::{
allocator::{
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
Allocator,
},
drm::{CreateDrmNodeError, DrmNode},
egl::{context::ContextPriority, EGLContext, EGLDisplay, Error as EGLError},
renderer::{
gles::{GlesError, GlesRenderer},
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
Renderer, RendererSuper,
},
SwapBuffersError,
};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use crate::{
backend::{
allocator::Buffer as BufferTrait,
egl::display::EGLBufferReader,
renderer::{
multigpu::{Error as MultigpuError, MultiRenderer, MultiTexture},
Bind, ExportMem, ImportDma, ImportEgl, ImportMem,
},
},
utils::{Buffer as BufferCoords, Rectangle},
};
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
use std::borrow::BorrowMut;
use std::{
collections::HashMap,
fmt,
os::unix::prelude::AsFd,
sync::atomic::{AtomicBool, Ordering},
};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Egl(#[from] EGLError),
#[error(transparent)]
Gl(#[from] GlesError),
#[error(transparent)]
DrmNode(#[from] CreateDrmNodeError),
}
impl From<Error> for SwapBuffersError {
#[inline]
fn from(err: Error) -> SwapBuffersError {
match err {
x @ Error::DrmNode(_) | x @ Error::Egl(_) => SwapBuffersError::ContextLost(Box::new(x)),
Error::Gl(x) => x.into(),
}
}
}
type Factory = Box<dyn Fn(&EGLDisplay) -> Result<GlesRenderer, Error>>;
pub struct GbmGlesBackend<R, A: AsFd + 'static> {
devices: HashMap<DrmNode, (EGLDisplay, GbmAllocator<A>)>,
factory: Option<Factory>,
context_priority: Option<ContextPriority>,
allocator_flags: GbmBufferFlags,
needs_enumeration: AtomicBool,
_renderer: std::marker::PhantomData<R>,
}
impl<R, A: AsFd + fmt::Debug + 'static> fmt::Debug for GbmGlesBackend<R, A> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmGlesBackend")
.field("devices", &self.devices)
.finish()
}
}
impl<R, A: AsFd + 'static> Default for GbmGlesBackend<R, A> {
#[inline]
fn default() -> Self {
GbmGlesBackend {
devices: HashMap::new(),
factory: None,
context_priority: None,
allocator_flags: GbmBufferFlags::RENDERING,
needs_enumeration: AtomicBool::new(true),
_renderer: std::marker::PhantomData,
}
}
}
impl<R, A: AsFd + Clone + Send + 'static> GbmGlesBackend<R, A> {
pub fn with_factory<F>(factory: F) -> Self
where
F: Fn(&EGLDisplay) -> Result<GlesRenderer, Error> + 'static,
{
Self {
factory: Some(Box::new(factory)),
..Default::default()
}
}
pub fn with_context_priority(priority: ContextPriority) -> Self {
Self {
context_priority: Some(priority),
..Default::default()
}
}
pub fn set_allocator_flags(&mut self, flags: GbmBufferFlags) {
self.allocator_flags = flags;
}
pub fn add_node(&mut self, node: DrmNode, gbm: GbmDevice<A>) -> Result<(), EGLError> {
if self.devices.contains_key(&node) {
return Ok(());
}
let allocator = GbmAllocator::new(gbm.clone(), self.allocator_flags);
self.devices
.insert(node, (unsafe { EGLDisplay::new(gbm)? }, allocator));
self.needs_enumeration.store(true, Ordering::SeqCst);
Ok(())
}
pub fn remove_node(&mut self, node: &DrmNode) {
if self.devices.remove(node).is_some() {
self.needs_enumeration.store(true, Ordering::SeqCst);
}
}
}
impl<R: From<GlesRenderer> + Renderer<Error = GlesError>, A: AsFd + Clone + 'static> GraphicsApi
for GbmGlesBackend<R, A>
{
type Device = GbmGlesDevice<R>;
type Error = Error;
fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
self.needs_enumeration.store(false, Ordering::SeqCst);
list.retain(|renderer| {
self.devices
.keys()
.any(|node| renderer.node.dev_id() == node.dev_id())
});
let new_renderers = self
.devices
.iter()
.filter(|(node, _)| {
!list
.iter()
.any(|renderer| renderer.node.dev_id() == node.dev_id())
})
.map(|(node, (display, gbm))| {
let renderer = if let Some(factory) = self.factory.as_ref() {
factory(display)?.into()
} else {
let context =
EGLContext::new_with_priority(display, self.context_priority.unwrap_or_default())
.map_err(Error::Egl)?;
unsafe { GlesRenderer::new(context).map_err(Error::Gl)? }.into()
};
Ok(GbmGlesDevice {
node: *node,
_display: display.clone(),
renderer,
allocator: Box::new(DmabufAllocator(gbm.clone())),
})
})
.flat_map(|x: Result<GbmGlesDevice<R>, Error>| match x {
Ok(x) => Some(x),
Err(x) => {
warn!("Skipping GbmDevice: {}", x);
None
}
})
.collect::<Vec<GbmGlesDevice<R>>>();
list.extend(new_renderers);
Ok(())
}
fn needs_enumeration(&self) -> bool {
self.needs_enumeration.load(Ordering::Acquire)
}
fn identifier() -> &'static str {
"gbm_gles"
}
}
impl<T: GraphicsApi, R: From<GlesRenderer> + Renderer<Error = GlesError>, A: AsFd + Clone + 'static>
std::convert::From<GlesError> for MultiError<GbmGlesBackend<R, A>, T>
where
T::Error: 'static,
<<T::Device as ApiDevice>::Renderer as RendererSuper>::Error: 'static,
{
#[inline]
fn from(err: GlesError) -> MultiError<GbmGlesBackend<R, A>, T> {
MultiError::Render(err)
}
}
pub struct GbmGlesDevice<R> {
node: DrmNode,
renderer: R,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
_display: EGLDisplay,
}
impl<R: Renderer> fmt::Debug for GbmGlesDevice<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmGlesDevice")
.field("node", &self.node)
.field("renderer", &self.renderer)
.finish_non_exhaustive()
}
}
impl<R: Renderer> ApiDevice for GbmGlesDevice<R> {
type Renderer = R;
fn renderer(&self) -> &Self::Renderer {
&self.renderer
}
fn renderer_mut(&mut self) -> &mut Self::Renderer {
&mut self.renderer
}
fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
self.allocator.as_mut()
}
fn node(&self) -> &DrmNode {
&self.node
}
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
impl<R, A> ImportEgl for MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
where
A: AsFd + Clone + 'static,
R: From<GlesRenderer>
+ BorrowMut<GlesRenderer>
+ Renderer<Error = GlesError>
+ Bind<Dmabuf>
+ ImportDma
+ ImportMem
+ ImportEgl
+ ExportMem
+ 'static,
R::TextureId: Clone + Send,
{
fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EGLError> {
self.render.renderer_mut().bind_wl_display(display)
}
fn unbind_wl_display(&mut self) {
self.render.renderer_mut().unbind_wl_display()
}
fn egl_reader(&self) -> Option<&EGLBufferReader> {
self.render.renderer().egl_reader()
}
#[profiling::function]
fn import_egl_buffer(
&mut self,
buffer: &wl_buffer::WlBuffer,
surface: Option<&crate::wayland::compositor::SurfaceData>,
damage: &[Rectangle<i32, BufferCoords>],
) -> Result<Self::TextureId, Self::Error> {
if let Some(dmabuf) = Self::try_import_egl(self.render.renderer_mut(), buffer)
.ok()
.or_else(|| {
self.target
.as_mut()
.and_then(|renderer| Self::try_import_egl(renderer.device.renderer_mut(), buffer).ok())
})
.or_else(|| {
self.other_renderers
.iter_mut()
.find_map(|renderer| Self::try_import_egl(renderer.renderer_mut(), buffer).ok())
})
{
let texture = MultiTexture::from_surface(surface, dmabuf.size(), dmabuf.format());
let texture_ref = texture.0.clone();
let res = self.import_dmabuf_internal(&dmabuf, texture, Some(damage));
if res.is_ok() {
if let Some(surface) = surface {
surface.data_map.insert_if_missing_threadsafe(|| texture_ref);
}
}
return res;
}
Err(MultigpuError::DeviceMissing)
}
}
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
impl<R, A> MultiRenderer<'_, '_, GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>
where
A: AsFd + Clone + 'static,
R: From<GlesRenderer>
+ BorrowMut<GlesRenderer>
+ Renderer<Error = GlesError>
+ ImportDma
+ ImportMem
+ ImportEgl
+ ExportMem
+ 'static,
{
#[profiling::function]
fn try_import_egl(
renderer: &mut R,
buffer: &wl_buffer::WlBuffer,
) -> Result<Dmabuf, MultigpuError<GbmGlesBackend<R, A>, GbmGlesBackend<R, A>>> {
if !renderer
.borrow_mut()
.extensions
.iter()
.any(|ext| ext == "GL_OES_EGL_image")
{
return Err(MultigpuError::Render(GlesError::GLExtensionNotSupported(&[
"GL_OES_EGL_image",
])));
}
if renderer.egl_reader().is_none() {
return Err(MultigpuError::Render(GlesError::EGLBufferAccessError(
crate::backend::egl::BufferAccessError::NotManaged(crate::backend::egl::EGLError::BadDisplay),
)));
}
unsafe {
renderer
.borrow_mut()
.egl_context()
.make_current()
.map_err(GlesError::from)
.map_err(MultigpuError::Render)?
};
let egl = renderer
.egl_reader()
.as_ref()
.unwrap()
.egl_buffer_contents(buffer)
.map_err(GlesError::EGLBufferAccessError)
.map_err(MultigpuError::Render)?;
renderer
.borrow_mut()
.egl_context()
.display()
.create_dmabuf_from_image(egl.image(0).unwrap(), egl.size, egl.y_inverted)
.map_err(GlesError::BindBufferEGLError)
.map_err(MultigpuError::Render)
}
}