212 lines
6.2 KiB
Rust
212 lines
6.2 KiB
Rust
// 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::{
|
|
gles::{GlesError, GlesRenderer, ffi},
|
|
glow::GlowRenderer,
|
|
multigpu::{ApiDevice, 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},
|
|
};
|
|
|
|
/// 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<Error> 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<A: AsFd + 'static> {
|
|
devices: HashMap<DrmNode, (GbmAllocator<A>, Cell<Option<GlowRenderer>>)>,
|
|
needs_enumeration: AtomicBool,
|
|
}
|
|
|
|
impl<A: AsFd + fmt::Debug + 'static> fmt::Debug for GbmGlowBackend<A> {
|
|
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<A: AsFd + 'static> Default for GbmGlowBackend<A> {
|
|
fn default() -> Self {
|
|
GbmGlowBackend {
|
|
devices: HashMap::new(),
|
|
needs_enumeration: AtomicBool::new(true),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<A: AsFd + Clone + Send + 'static> GbmGlowBackend<A> {
|
|
pub fn new() -> Self {
|
|
GbmGlowBackend {
|
|
devices: HashMap::new(),
|
|
needs_enumeration: AtomicBool::new(false),
|
|
}
|
|
}
|
|
|
|
pub fn current_devices(&self) -> impl Iterator<Item = &DrmNode> {
|
|
self.devices.keys()
|
|
}
|
|
|
|
pub fn add_node(&mut self, node: DrmNode, gbm: GbmAllocator<A>, 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<A: AsFd + Device + Clone + 'static> GraphicsApi for GbmGlowBackend<A> {
|
|
type Device = GbmGlowDevice;
|
|
type Error = Error;
|
|
|
|
fn enumerate(&self, list: &mut Vec<Self::Device>) -> 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::<GlesRenderer>::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::<Vec<GbmGlowDevice>>();
|
|
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<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
|
|
}
|
|
|
|
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<Buffer = Dmabuf, Error = AnyError> {
|
|
self.allocator.as_mut()
|
|
}
|
|
fn node(&self) -> &DrmNode {
|
|
&self.node
|
|
}
|
|
fn can_do_cross_device_imports(&self) -> bool {
|
|
!Borrow::<GlesRenderer>::borrow(&self.renderer).is_software()
|
|
}
|
|
|
|
fn should_do_cross_device_exports(&self) -> bool {
|
|
!self.intel_export_quirk
|
|
}
|
|
}
|