aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/tty.rs97
1 files changed, 71 insertions, 26 deletions
diff --git a/src/backend/tty.rs b/src/backend/tty.rs
index d0626cd5..693b7d6f 100644
--- a/src/backend/tty.rs
+++ b/src/backend/tty.rs
@@ -132,7 +132,8 @@ type GbmDrmCompositor = DrmCompositor<
pub struct OutputDevice {
token: RegistrationToken,
- render_node: DrmNode,
+ // Can be None for display-only devices such as DisplayLink.
+ render_node: Option<DrmNode>,
drm_scanner: DrmScanner,
surfaces: HashMap<crtc::Handle, Surface>,
known_crtcs: HashMap<crtc::Handle, CrtcInfo>,
@@ -140,6 +141,7 @@ pub struct OutputDevice {
// See https://github.com/Smithay/smithay/issues/1102.
drm: DrmDevice,
gbm: GbmDevice<DrmDeviceFd>,
+ // For display-only devices this will be the allocator from the primary device.
allocator: GbmAllocator<DrmDeviceFd>,
pub drm_lease_state: Option<DrmLeaseState>,
@@ -751,18 +753,39 @@ impl Tty {
GbmDevice::new(device_fd)
}?;
- let display = unsafe { EGLDisplay::new(gbm.clone())? };
- let egl_device = EGLDevice::device_for_display(&display)?;
+ let mut try_initialize_gpu = || {
+ let display = unsafe { EGLDisplay::new(gbm.clone())? };
+ let egl_device = EGLDevice::device_for_display(&display)?;
- let render_node = egl_device
- .try_get_render_node()?
- .context("no render node")?;
- self.gpu_manager
- .as_mut()
- .add_node(render_node, gbm.clone())
- .context("error adding render node to GPU manager")?;
+ // Software EGL devices (e.g., llvmpipe/softpipe) are rejected for now. They have some
+ // problems (segfault on importing dmabufs from other renderers) and need to be
+ // excluded from some places like DRM leasing.
+ ensure!(
+ !egl_device.is_software(),
+ "software EGL renderers are skipped"
+ );
+
+ let render_node = egl_device
+ .try_get_render_node()
+ .ok()
+ .flatten()
+ .unwrap_or(node);
+ self.gpu_manager
+ .as_mut()
+ .add_node(render_node, gbm.clone())
+ .context("error adding render node to GPU manager")?;
+
+ Ok(render_node)
+ };
+
+ let render_node = try_initialize_gpu()
+ .inspect_err(|err| {
+ debug!("failed to initialize renderer, falling back to primary gpu: {err:?}");
+ })
+ .ok();
- if render_node == self.primary_render_node {
+ if render_node == Some(self.primary_render_node) {
+ let render_node = self.primary_render_node;
debug!("this is the primary render node");
let mut renderer = self
@@ -807,13 +830,14 @@ impl Tty {
assert!(self.dmabuf_global.replace(dmabuf_global).is_none());
// Update the dmabuf feedbacks for all surfaces.
- for device in self.devices.values_mut() {
+ for (node, device) in self.devices.iter_mut() {
for surface in device.surfaces.values_mut() {
match surface_dmabuf_feedback(
&surface.compositor,
primary_formats.clone(),
self.primary_render_node,
device.render_node,
+ *node,
) {
Ok(feedback) => {
surface.dmabuf_feedback = Some(feedback);
@@ -826,8 +850,15 @@ impl Tty {
}
}
+ let allocator_gbm = if render_node.is_some() {
+ gbm.clone()
+ } else if let Some(primary_device) = self.devices.get(&self.primary_node) {
+ primary_device.gbm.clone()
+ } else {
+ bail!("no allocator available for device");
+ };
let gbm_flags = GbmBufferFlags::RENDERING | GbmBufferFlags::SCANOUT;
- let allocator = GbmAllocator::new(gbm.clone(), gbm_flags);
+ let allocator = GbmAllocator::new(allocator_gbm, gbm_flags);
let token = niri
.event_loop
@@ -1065,8 +1096,8 @@ impl Tty {
lease_state.disable_global::<State>();
}
- if device.render_node == self.primary_render_node {
- match self.gpu_manager.single_renderer(&device.render_node) {
+ if device.render_node == Some(self.primary_render_node) {
+ match self.gpu_manager.single_renderer(&self.primary_render_node) {
Ok(mut renderer) => renderer.unbind_wl_display(),
Err(err) => {
warn!("error creating renderer during device removal: {err}");
@@ -1101,9 +1132,11 @@ impl Tty {
}
}
- self.gpu_manager.as_mut().remove_node(&device.render_node);
- // Trigger re-enumeration in order to remove the device from gpu_manager.
- let _ = self.gpu_manager.devices();
+ if let Some(render_node) = device.render_node {
+ self.gpu_manager.as_mut().remove_node(&render_node);
+ // Trigger re-enumeration in order to remove the device from gpu_manager.
+ let _ = self.gpu_manager.devices();
+ }
niri.event_loop.remove(device.token);
@@ -1296,13 +1329,16 @@ impl Tty {
output.user_data().insert_if_missing(|| PanelOrientation(x));
}
- let renderer = self.gpu_manager.single_renderer(&device.render_node)?;
+ let render_node = device.render_node.unwrap_or(self.primary_render_node);
+ let renderer = self.gpu_manager.single_renderer(&render_node)?;
let egl_context = renderer.as_ref().egl_context();
let render_formats = egl_context.dmabuf_render_formats();
// Filter out the CCS modifiers as they have increased bandwidth, causing some monitor
// configurations to stop working.
//
+ // For display only devices, restrict to linear buffers for best compatibility.
+ //
// The invalid modifier attempt below should make this unnecessary in some cases, but it
// would still be a bad idea to remove this until Smithay has some kind of full-device
// modesetting test that is able to "downgrade" existing connector modifiers to get enough
@@ -1311,6 +1347,10 @@ impl Tty {
.iter()
.copied()
.filter(|format| {
+ if device.render_node.is_none() {
+ return format.modifier == Modifier::Linear;
+ }
+
let is_ccs = matches!(
format.modifier,
Modifier::I915_y_tiled_ccs
@@ -1391,6 +1431,7 @@ impl Tty {
primary_formats,
self.primary_render_node,
device.render_node,
+ node,
) {
Ok(feedback) => {
dmabuf_feedback = Some(feedback);
@@ -1747,7 +1788,7 @@ impl Tty {
let mut renderer = match self.gpu_manager.renderer(
&self.primary_render_node,
- &device.render_node,
+ &device.render_node.unwrap_or(self.primary_render_node),
surface.compositor.format(),
) {
Ok(renderer) => renderer,
@@ -2099,7 +2140,7 @@ impl Tty {
let device = self
.devices
.values()
- .find(|d| d.render_node == self.primary_render_node);
+ .find(|d| d.render_node == Some(self.primary_render_node));
// Otherwise, try to get the device corresponding to the primary node.
let device = device.or_else(|| self.devices.get(&self.primary_node));
@@ -2650,7 +2691,8 @@ fn surface_dmabuf_feedback(
compositor: &GbmDrmCompositor,
primary_formats: FormatSet,
primary_render_node: DrmNode,
- surface_render_node: DrmNode,
+ surface_render_node: Option<DrmNode>,
+ surface_scanout_node: DrmNode,
) -> Result<SurfaceDmabufFeedback, io::Error> {
let surface = compositor.surface();
let planes = surface.planes();
@@ -2675,7 +2717,10 @@ fn surface_dmabuf_feedback(
// HACK: AMD iGPU + dGPU systems share some modifiers between the two, and yet cross-device
// buffers produce a glitched scanout if the modifier is not Linear...
- if primary_render_node != surface_render_node {
+ //
+ // Also limit scan-out formats to Linear if we have a device without a render node (i.e.
+ // we're rendering on a different device).
+ if surface_render_node != Some(primary_render_node) {
primary_scanout_formats.retain(|f| f.modifier == Modifier::Linear);
primary_or_overlay_scanout_formats.retain(|f| f.modifier == Modifier::Linear);
}
@@ -2694,12 +2739,12 @@ fn surface_dmabuf_feedback(
let scanout = builder
.clone()
.add_preference_tranche(
- surface_render_node.dev_id(),
+ surface_scanout_node.dev_id(),
Some(TrancheFlags::Scanout),
primary_scanout_formats,
)
.add_preference_tranche(
- surface_render_node.dev_id(),
+ surface_scanout_node.dev_id(),
Some(TrancheFlags::Scanout),
primary_or_overlay_scanout_formats,
)
@@ -2707,7 +2752,7 @@ fn surface_dmabuf_feedback(
// If this is the primary node surface, send scanout formats in both tranches to avoid
// duplication.
- let render = if primary_render_node == surface_render_node {
+ let render = if surface_render_node == Some(primary_render_node) {
scanout.clone()
} else {
builder.build()?