Skip to content

Commit d808432

Browse files
committed
multigpu: add drm/pixman based graphics api
1 parent 950d0d7 commit d808432

File tree

2 files changed

+224
-0
lines changed

2 files changed

+224
-0
lines changed

src/backend/renderer/multigpu/drm.rs

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
//! Implementation of the multi-gpu [`GraphicsApi`] using
2+
//! user provided DRM devices and pixman for rendering.
3+
4+
use std::fmt;
5+
use std::sync::atomic::Ordering;
6+
use std::{collections::HashMap, sync::atomic::AtomicBool};
7+
8+
use drm::node::{CreateDrmNodeError, DrmNode};
9+
use tracing::warn;
10+
use wayland_server::protocol::wl_buffer;
11+
12+
use crate::backend::allocator::dmabuf::DmabufAllocator;
13+
use crate::backend::renderer::pixman::PixmanError;
14+
use crate::backend::renderer::{ImportDma, ImportMem, Renderer};
15+
use crate::backend::SwapBuffersError;
16+
use crate::backend::{
17+
allocator::{
18+
dmabuf::{AnyError, Dmabuf},
19+
dumb::DumbAllocator,
20+
Allocator,
21+
},
22+
drm::DrmDevice,
23+
renderer::pixman::PixmanRenderer,
24+
};
25+
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
26+
use crate::backend::{
27+
egl::{display::EGLBufferReader, Error as EGLError},
28+
renderer::{multigpu::Error as MultigpuError, ImportEgl},
29+
};
30+
use crate::utils::{Buffer as BufferCoords, Rectangle};
31+
32+
use super::{ApiDevice, GraphicsApi, MultiRenderer, TryImportEgl};
33+
34+
/// Errors raised by the [`DrmPixmanBackend`]
35+
#[derive(Debug, thiserror::Error)]
36+
pub enum Error {
37+
/// Pixman error
38+
#[error(transparent)]
39+
Pixman(#[from] PixmanError),
40+
/// Error creating a drm node
41+
#[error(transparent)]
42+
DrmNode(#[from] CreateDrmNodeError),
43+
}
44+
45+
impl From<Error> for SwapBuffersError {
46+
#[inline]
47+
fn from(err: Error) -> SwapBuffersError {
48+
match err {
49+
x @ Error::DrmNode(_) => SwapBuffersError::ContextLost(Box::new(x)),
50+
Error::Pixman(x) => x.into(),
51+
}
52+
}
53+
}
54+
55+
/// A [`GraphicsApi`] utilizing user-provided DRM Devices and Pixman for rendering.
56+
#[derive(Debug)]
57+
pub struct DrmPixmanBackend {
58+
devices: HashMap<DrmNode, DumbAllocator>,
59+
needs_enumeration: AtomicBool,
60+
}
61+
62+
impl DrmPixmanBackend {
63+
/// Add a new DRM device for a given node to the api
64+
pub fn add_node(&mut self, node: DrmNode, drm: &DrmDevice) {
65+
if self.devices.contains_key(&node) {
66+
return;
67+
}
68+
69+
let allocator = DumbAllocator::new(drm.device_fd().clone());
70+
self.devices.insert(node, allocator);
71+
self.needs_enumeration.store(true, Ordering::SeqCst);
72+
}
73+
74+
/// Remove a given node from the api
75+
pub fn remove_node(&mut self, node: &DrmNode) {
76+
if self.devices.remove(node).is_some() {
77+
self.needs_enumeration.store(true, Ordering::SeqCst);
78+
}
79+
}
80+
}
81+
82+
impl Default for DrmPixmanBackend {
83+
#[inline]
84+
fn default() -> Self {
85+
Self {
86+
devices: Default::default(),
87+
needs_enumeration: AtomicBool::new(true),
88+
}
89+
}
90+
}
91+
92+
impl GraphicsApi for DrmPixmanBackend {
93+
type Device = DrmPixmanDevice;
94+
95+
type Error = Error;
96+
97+
fn enumerate(&self, list: &mut Vec<Self::Device>) -> Result<(), Self::Error> {
98+
self.needs_enumeration.store(false, Ordering::SeqCst);
99+
100+
// remove old stuff
101+
list.retain(|renderer| {
102+
self.devices
103+
.keys()
104+
.any(|node| renderer.node.dev_id() == node.dev_id())
105+
});
106+
107+
// add new stuff
108+
let new_renderers = self
109+
.devices
110+
.iter()
111+
.filter(|(node, _)| {
112+
!list
113+
.iter()
114+
.any(|renderer| renderer.node.dev_id() == node.dev_id())
115+
})
116+
.map(|(node, drm)| {
117+
let renderer = PixmanRenderer::new()?;
118+
119+
Ok(DrmPixmanDevice {
120+
node: *node,
121+
renderer,
122+
allocator: Box::new(DmabufAllocator(drm.clone())),
123+
})
124+
})
125+
.flat_map(|x: Result<DrmPixmanDevice, Error>| match x {
126+
Ok(x) => Some(x),
127+
Err(x) => {
128+
warn!("Skipping DrmDevice: {}", x);
129+
None
130+
}
131+
})
132+
.collect::<Vec<DrmPixmanDevice>>();
133+
list.extend(new_renderers);
134+
135+
// but don't replace already initialized renderers
136+
137+
Ok(())
138+
}
139+
140+
fn needs_enumeration(&self) -> bool {
141+
self.needs_enumeration.load(Ordering::Acquire)
142+
}
143+
144+
fn identifier() -> &'static str {
145+
"drm_pixman"
146+
}
147+
}
148+
149+
/// [`ApiDevice`] of the [`DrmPixmanBackend`]
150+
pub struct DrmPixmanDevice {
151+
node: DrmNode,
152+
renderer: PixmanRenderer,
153+
allocator: Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>,
154+
}
155+
156+
impl fmt::Debug for DrmPixmanDevice {
157+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158+
f.debug_struct("DrmPixmanDevice")
159+
.field("node", &self.node)
160+
.field("renderer", &self.renderer)
161+
.finish()
162+
}
163+
}
164+
165+
impl ApiDevice for DrmPixmanDevice {
166+
type Renderer = PixmanRenderer;
167+
168+
fn renderer(&self) -> &Self::Renderer {
169+
&self.renderer
170+
}
171+
172+
fn renderer_mut(&mut self) -> &mut Self::Renderer {
173+
&mut self.renderer
174+
}
175+
176+
fn allocator(&mut self) -> &mut dyn Allocator<Buffer = Dmabuf, Error = AnyError> {
177+
&mut self.allocator
178+
}
179+
180+
fn node(&self) -> &DrmNode {
181+
&self.node
182+
}
183+
}
184+
185+
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
186+
impl TryImportEgl<PixmanRenderer> for DrmPixmanDevice {
187+
type Error = PixmanError;
188+
189+
fn try_import_egl(_renderer: &mut PixmanRenderer, _buffer: &wl_buffer::WlBuffer) -> Result<Dmabuf, Self::Error> {
190+
return Err(PixmanError::Unsupported)
191+
}
192+
}
193+
194+
#[cfg(all(feature = "wayland_frontend", feature = "use_system_lib"))]
195+
impl<T> ImportEgl for MultiRenderer<'_, '_, DrmPixmanBackend, T>
196+
where
197+
T: GraphicsApi,
198+
<T as GraphicsApi>::Error: 'static,
199+
<<T as GraphicsApi>::Device as ApiDevice>::Renderer: ImportDma + ImportMem,
200+
<<<T as GraphicsApi>::Device as ApiDevice>::Renderer as Renderer>::Error: 'static,
201+
{
202+
fn bind_wl_display(&mut self, display: &wayland_server::DisplayHandle) -> Result<(), EGLError> {
203+
self.render.renderer_mut().bind_wl_display(display)
204+
}
205+
fn unbind_wl_display(&mut self) {
206+
self.render.renderer_mut().unbind_wl_display()
207+
}
208+
fn egl_reader(&self) -> Option<&EGLBufferReader> {
209+
self.render.renderer().egl_reader()
210+
}
211+
212+
#[profiling::function]
213+
fn import_egl_buffer(
214+
&mut self,
215+
_buffer: &wl_buffer::WlBuffer,
216+
_surface: Option<&crate::wayland::compositor::SurfaceData>,
217+
_damage: &[Rectangle<i32, BufferCoords>],
218+
) -> Result<<Self as Renderer>::TextureId, <Self as Renderer>::Error> {
219+
Err(MultigpuError::Render(PixmanError::Unsupported))
220+
}
221+
}

src/backend/renderer/multigpu/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ use wayland_server::protocol::{wl_buffer, wl_shm, wl_surface::WlSurface};
7878
#[cfg(all(feature = "backend_gbm", feature = "backend_egl", feature = "renderer_gl"))]
7979
pub mod gbm;
8080

81+
#[cfg(all(feature = "backend_drm", feature = "renderer_pixman"))]
82+
pub mod drm;
83+
8184
/// Tracks available gpus from a given [`GraphicsApi`]
8285
#[derive(Debug)]
8386
pub struct GpuManager<A: GraphicsApi> {

0 commit comments

Comments
 (0)