// SPDX-License-Identifier: GPL-3.0-only use clap_lex::OsStrExt; use rustix::path::Arg; use smithay::{ backend::{ SwapBuffersError, allocator::{ Allocator, dmabuf::{AnyError, Dmabuf, DmabufAllocator}, gbm::GbmAllocator, }, drm::{CreateDrmNodeError, DrmNode}, renderer::{ RendererSuper, gles::{GlesError, GlesRenderer, ffi}, glow::GlowRenderer, multigpu::{ApiDevice, Error as MultiError, GraphicsApi}, }, }, reexports::drm::control::Device, }; use std::{ borrow::{Borrow, BorrowMut}, cell::Cell, ffi::{CStr, c_char}, }; use std::{ collections::HashMap, fmt, os::unix::prelude::AsFd, sync::atomic::{AtomicBool, Ordering}, }; use crate::backend::render::element::FromGlesError; /// Errors raised by the [`GbmGlesBackend`] #[derive(Debug, thiserror::Error)] pub enum Error { /// OpenGL error #[error(transparent)] Gl(#[from] GlesError), /// Error creating a drm node #[error(transparent)] DrmNode(#[from] CreateDrmNodeError), } impl From for SwapBuffersError { fn from(err: Error) -> SwapBuffersError { match err { x @ Error::DrmNode(_) => SwapBuffersError::ContextLost(Box::new(x)), Error::Gl(x) => x.into(), } } } /// A [`GraphicsApi`] utilizing user-provided GBM Devices and OpenGL ES for rendering. pub struct GbmGlowBackend { devices: HashMap, Cell>)>, needs_enumeration: AtomicBool, } impl fmt::Debug for GbmGlowBackend { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("GbmGlesBackend") .field("devices", &self.devices.keys()) .field("needs_enumeration", &self.needs_enumeration) .finish() } } impl Default for GbmGlowBackend { fn default() -> Self { GbmGlowBackend { devices: HashMap::new(), needs_enumeration: AtomicBool::new(true), } } } impl GbmGlowBackend { pub fn new() -> Self { GbmGlowBackend { devices: HashMap::new(), needs_enumeration: AtomicBool::new(false), } } pub fn current_devices(&self) -> impl Iterator { self.devices.keys() } pub fn add_node(&mut self, node: DrmNode, gbm: GbmAllocator, renderer: GlowRenderer) { if self.devices.contains_key(&node) { return; } self.devices.insert(node, (gbm, Cell::new(Some(renderer)))); self.needs_enumeration.store(true, Ordering::SeqCst); } /// Remove a given node from the api pub fn remove_node(&mut self, node: &DrmNode) { if self.devices.remove(node).is_some() { self.needs_enumeration.store(true, Ordering::SeqCst); } } } impl GraphicsApi for GbmGlowBackend { type Device = GbmGlowDevice; type Error = Error; fn enumerate(&self, list: &mut Vec) -> Result<(), Self::Error> { self.needs_enumeration.store(false, Ordering::SeqCst); // remove old stuff list.retain(|renderer| { self.devices .keys() .any(|node| renderer.node.dev_id() == node.dev_id()) }); // add new stuff let new_renderers = self .devices .iter() // but don't replace already initialized renderers .filter(|(node, _)| { !list .iter() .any(|renderer| renderer.node.dev_id() == node.dev_id()) }) .flat_map(|(node, (allocator, renderer))| { let mut renderer = renderer.replace(None)?; let is_intel = allocator .as_ref() .get_driver() .is_ok_and(|drv| drv.name().contains("i915")); let intel_export_quirk = is_intel && BorrowMut::::borrow_mut(&mut renderer) .with_context(|gl| unsafe { CStr::from_ptr(gl.GetString(ffi::RENDERER) as *const c_char) }) .is_ok_and(|name| { name.as_str().is_ok_and(|name| { name.contains("TGL") || name.contains("MTL") || name.contains("ARL") || name.contains("LNL") }) }); Some(GbmGlowDevice { node: *node, renderer, intel_export_quirk, allocator: Box::new(DmabufAllocator(allocator.clone())), }) }) .collect::>(); list.extend(new_renderers); Ok(()) } fn needs_enumeration(&self) -> bool { self.needs_enumeration.load(Ordering::Acquire) } fn identifier() -> &'static str { "gbm_glow" } } /// [`ApiDevice`] of the [`GbmGlesBackend`] pub struct GbmGlowDevice { node: DrmNode, renderer: GlowRenderer, intel_export_quirk: bool, allocator: Box>, } impl fmt::Debug for GbmGlowDevice { 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 ApiDevice for GbmGlowDevice { type Renderer = GlowRenderer; 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 { self.allocator.as_mut() } fn node(&self) -> &DrmNode { &self.node } fn can_do_cross_device_imports(&self) -> bool { !Borrow::::borrow(&self.renderer).is_software() } fn should_do_cross_device_exports(&self) -> bool { !self.intel_export_quirk } } impl FromGlesError for MultiError, T> where T::Error: 'static, <::Renderer as RendererSuper>::Error: 'static, { #[inline] fn from_gles_error(err: GlesError) -> MultiError, T> { MultiError::Render(err) } }