kms: New backend
New backend utilizing a thread per surface for precise frame scheduling.
This commit is contained in:
parent
3b7bba3add
commit
469a366207
24 changed files with 3219 additions and 1958 deletions
191
src/backend/kms/render/gles.rs
Normal file
191
src/backend/kms/render/gles.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
2
src/backend/kms/render/mod.rs
Normal file
2
src/backend/kms/render/mod.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod gles;
|
||||
pub mod pixman;
|
||||
155
src/backend/kms/render/pixman.rs
Normal file
155
src/backend/kms/render/pixman.rs
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue