kms: New backend

New backend utilizing a thread per surface for precise frame
scheduling.
This commit is contained in:
Victoria Brekenfeld 2024-06-07 20:04:39 +02:00 committed by Victoria Brekenfeld
parent 3b7bba3add
commit 469a366207
24 changed files with 3219 additions and 1958 deletions

View file

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-3.0-only
use smithay::backend::{
allocator::{
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::GbmAllocator,
Allocator,
},
drm::{CreateDrmNodeError, DrmNode},
renderer::{
gles::GlesError,
glow::GlowRenderer,
multigpu::{ApiDevice, Error as MultiError, GraphicsApi},
Renderer,
},
SwapBuffersError,
};
use std::cell::Cell;
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<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 + 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 renderer = renderer.replace(None)?;
Some(GbmGlowDevice {
node: *node,
renderer,
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,
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
}
}
impl<T: GraphicsApi, A: AsFd + Clone + 'static> FromGlesError for MultiError<GbmGlowBackend<A>, T>
where
T::Error: 'static,
<<T::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
{
#[inline]
fn from_gles_error(err: GlesError) -> MultiError<GbmGlowBackend<A>, T> {
MultiError::Render(err)
}
}

View file

@ -0,0 +1,2 @@
pub mod gles;
pub mod pixman;

View file

@ -0,0 +1,155 @@
use std::{
collections::HashMap,
fmt,
os::fd::AsFd,
sync::atomic::{AtomicBool, Ordering},
};
use smithay::backend::{
allocator::{
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
Allocator,
},
drm::DrmNode,
renderer::{
multigpu::{ApiDevice, GraphicsApi},
pixman::{PixmanError, PixmanRenderer},
},
};
use tracing::warn;
#[derive(Debug)]
pub struct GbmPixmanBackend<A: AsFd + 'static> {
devices: HashMap<DrmNode, GbmAllocator<A>>,
needs_enumeration: AtomicBool,
allocator_flags: GbmBufferFlags,
}
pub struct GbmPixmanDevice {
node: DrmNode,
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
renderer: PixmanRenderer,
}
impl fmt::Debug for GbmPixmanDevice {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("GbmPixmanDevice")
.field("node", &self.node)
.field("allocator", &"...")
.field("renderer", &self.renderer)
.finish()
}
}
impl<A: AsFd + 'static> GbmPixmanBackend<A> {
pub fn new() -> Self {
GbmPixmanBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(false),
allocator_flags: GbmBufferFlags::RENDERING,
}
}
pub fn with_allocator_flags(allocator_flags: GbmBufferFlags) -> Self {
GbmPixmanBackend {
devices: HashMap::new(),
needs_enumeration: AtomicBool::new(false),
allocator_flags,
}
}
pub fn set_allocator_flags(&mut self, flags: GbmBufferFlags) {
self.allocator_flags = flags;
}
pub fn add_node(&mut self, node: DrmNode, gbm: GbmDevice<A>) {
if self.devices.contains_key(&node) {
return;
}
let allocator = GbmAllocator::new(gbm, self.allocator_flags);
self.devices.insert(node, allocator);
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 + Clone + 'static> GraphicsApi for GbmPixmanBackend<A> {
type Device = GbmPixmanDevice;
type Error = PixmanError;
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())
});
// add new stuff
let new_renderers = self
.devices
.iter()
.filter(|(node, _)| {
!list
.iter()
.any(|renderer| renderer.node.dev_id() == node.dev_id())
})
.map(|(node, gbm)| {
Ok(GbmPixmanDevice {
node: *node,
allocator: Box::new(DmabufAllocator(gbm.clone()))
as Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
renderer: PixmanRenderer::new()?,
})
})
.flat_map(|x: Result<GbmPixmanDevice, PixmanError>| match x {
Ok(x) => Some(x),
Err(x) => {
warn!("Skipping pixman device: {}", x);
None
}
})
.collect::<Vec<GbmPixmanDevice>>();
list.extend(new_renderers);
Ok(())
}
fn identifier() -> &'static str {
"gbm_pixman"
}
fn needs_enumeration(&self) -> bool {
self.needs_enumeration.load(Ordering::SeqCst)
}
}
impl ApiDevice for GbmPixmanDevice {
type Renderer = PixmanRenderer;
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> {
&mut self.allocator
}
fn node(&self) -> &DrmNode {
&self.node
}
}