atagen

monolith

public
Star0
Watch0

Commit 2454f8b9122d

tarball

atagen <boss@atagen.co>2026-10-19 06:06 UTC

3 files changed+43-2
dmabuf: advertise the render node as default feedback

Without a feedback main device, Mesa EGL and egl-wayland cannot find a GPU, so Qt clients like quickshell fell back to wl_shm. That cost a CPU upload per frame and kept their full-output pools mapped in the compositor.
Modified file: crates/monolith/src/globals.rs+16 additions−2 deletions
@@ -9,7 +9,7 @@ use smithay::backend::renderer::ImportDma;
99 use smithay::reexports::wayland_server::DisplayHandle;
1010 use smithay::wayland::compositor::CompositorState;
1111 use smithay::wayland::cursor_shape::CursorShapeManagerState;
12-use smithay::wayland::dmabuf::{DmabufGlobal, DmabufState};
12+use smithay::wayland::dmabuf::{DmabufFeedbackBuilder, DmabufGlobal, DmabufState};
1313 use smithay::wayland::foreign_toplevel_list::ForeignToplevelListState;
1414 use smithay::wayland::idle_inhibit::IdleInhibitManagerState;
1515 use smithay::wayland::image_capture_source::{
@@ -59,7 +59,21 @@ pub struct Protocols {
5959 impl Protocols {
6060 pub fn new(dh: &DisplayHandle, renderer: &VulkanRenderer) -> Self {
6161 let mut dmabuf_state = DmabufState::new();
62- let dmabuf_global = dmabuf_state.create_global::<State>(dh, renderer.dmabuf_formats());
62+ let formats = renderer.dmabuf_formats();
63+ // EGL finds its device through the feedback main device, and without
64+ // one Mesa and egl-wayland give up and Qt falls back to wl_shm.
65+ let feedback = renderer.drm_node().and_then(|node| {
66+ DmabufFeedbackBuilder::new(node, formats.iter().copied())
67+ .build()
68+ .inspect_err(|e| tracing::warn!("no dmabuf feedback: {e}"))
69+ .ok()
70+ });
71+ let dmabuf_global = match &feedback {
72+ Some(feedback) => {
73+ dmabuf_state.create_global_with_default_feedback::<State>(dh, feedback)
74+ }
75+ None => dmabuf_state.create_global::<State>(dh, formats),
76+ };
6377 let primary_selection_state = PrimarySelectionState::new::<State>(dh);
6478 Self {
6579 compositor_state: CompositorState::new::<State>(dh),
Modified file: crates/render-vk/src/device.rs+22 additions
@@ -54,6 +54,7 @@ pub(crate) struct Device {
5454 pub push_descriptor: ash::khr::push_descriptor::Device,
5555 pub dmabuf: Option<DmabufExt>,
5656 pub name: String,
57+ pub drm_node: Option<u64>,
5758 /// Signalled fences kept for reuse, see `FenceInner`.
5859 pub spare_fences: Mutex<Vec<vk::Fence>>,
5960 }
@@ -104,6 +105,11 @@ impl Shared {
104105 .iter()
105106 .filter_map(|e| e.extension_name_as_c_str().ok().map(ToOwned::to_owned))
106107 .collect();
108+ let drm_node = available
109+ .iter()
110+ .any(|a| a.as_c_str() == ash::ext::physical_device_drm::NAME)
111+ .then(|| unsafe { drm_node(&instance, physical) })
112+ .flatten();
107113 let has_dmabuf = DMABUF_EXTENSIONS
108114 .iter()
109115 .all(|n| available.iter().any(|a| a.as_c_str() == *n));
@@ -185,6 +191,7 @@ impl Shared {
185191 push_descriptor,
186192 dmabuf,
187193 name,
194+ drm_node,
188195 spare_fences: Mutex::new(Vec::new()),
189196 };
190197 Ok(Self {
@@ -209,6 +216,21 @@ unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, no
209216 || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))
210217 }
211218
219+/// The render node of `physical`, or its primary node when it has none.
220+unsafe fn drm_node(instance: &ash::Instance, physical: vk::PhysicalDevice) -> Option<u64> {
221+ let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();
222+ let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);
223+ unsafe { instance.get_physical_device_properties2(physical, &mut props) };
224+ let node = |major: i64, minor: i64| rustix::fs::makedev(major as u32, minor as u32);
225+ if drm.has_render == vk::TRUE {
226+ Some(node(drm.render_major, drm.render_minor))
227+ } else if drm.has_primary == vk::TRUE {
228+ Some(node(drm.primary_major, drm.primary_minor))
229+ } else {
230+ None
231+ }
232+}
233+
212234 unsafe fn pick_physical(
213235 instance: &ash::Instance,
214236 wanted: Option<&str>,
Modified file: crates/render-vk/src/renderer/mod.rs+5 additions
@@ -138,6 +138,11 @@ impl VulkanRenderer {
138138 &self.shared.device.name
139139 }
140140
141+ /// DRM device number of the GPU in use, its render node where it has one.
142+ pub fn drm_node(&self) -> Option<u64> {
143+ self.shared.device.drm_node
144+ }
145+
141146 /// Whether dmabufs can be imported, rendered into and allocated.
142147 pub fn supports_dmabuf(&self) -> bool {
143148 self.caps.render.iter().next().is_some()