Commit f27fa4c2ade9
tarball6 files changed+77-21
@@ -32,6 +32,7 @@ use smithay::reexports::rustix::fs::OFlags;
3232
use smithay::utils::{DeviceFd, Point, Transform};3333
use smithay_drm_extras::drm_scanner::{DrmScanEvent, DrmScanner};3434
use std::collections::HashMap;35
+use std::path::{Path, PathBuf};3536
use std::time::Duration;3637
3738
const CLEAR: [f32; 4] = [0.07, 0.075, 0.09, 1.0];@@ -75,20 +76,27 @@ impl Surface {
7576
}7677
}7778
79
+/// The GPU monolith drives on bare metal, the seat's primary or else its80
+/// first.81
+pub fn primary() -> Result<PathBuf, Box<dyn std::error::Error>> {82
+ let seat = std::env::var("XDG_SEAT").unwrap_or_else(|_| "seat0".into());83
+ Ok(match primary_gpu(&seat)? {84
+ Some(p) => p,85
+ None => all_gpus(&seat)?.into_iter().next().ok_or("no GPU found")?,86
+ })87
+}88
+7889
pub fn init(7990
event_loop: &mut EventLoop<'static, State>,8091
state: &mut State,92
+ path: &Path,8193
) -> Result<(), Box<dyn std::error::Error>> {8294
let (mut session, notifier) = LibSeatSession::new()?;8395
let seat = session.seat();84
- let path = match primary_gpu(&seat)? {85
- Some(p) => p,86
- None => all_gpus(&seat)?.into_iter().next().ok_or("no GPU found")?,87
- };88
- let node = DrmNode::from_path(&path)?;96
+ let node = DrmNode::from_path(path)?;8997
tracing::info!(path = %path.display(), "primary GPU");9098
let fd = session.open(91
- &path,99
+ path,92100
OFlags::RDWR | OFlags::CLOEXEC | OFlags::NOCTTY | OFlags::NONBLOCK,93101
)?;94102
let fd = DrmDeviceFd::new(DeviceFd::from(fd));@@ -117,10 +117,19 @@ fn run(
117117
) -> Result<(), Box<dyn std::error::Error>> {118118
let mut event_loop: EventLoop<'static, State> = EventLoop::try_new()?;119119
let display: Display<State> = Display::new()?;120
- let mut state = State::new(&mut event_loop, display, config)?;121
- match backend {122
- Backend::Drm => drm::init(&mut event_loop, &mut state)?,123
- _ => nested::init(&mut event_loop, &mut state)?,120
+ let gpu = match backend {121
+ Backend::Drm => Some(drm::primary()?),122
+ _ => None,123
+ };124
+ let node = gpu125
+ .as_deref()126
+ .map(smithay::backend::drm::DrmNode::from_path)127
+ .transpose()?128
+ .map(|n| n.dev_id());129
+ let mut state = State::new(&mut event_loop, display, config, node)?;130
+ match &gpu {131
+ Some(path) => drm::init(&mut event_loop, &mut state, path)?,132
+ None => nested::init(&mut event_loop, &mut state)?,124133
}125134
ipc::init(&event_loop, &mut state)?;126135
tracing::info!(socket = ?state.socket_name, device = state.renderer.device_name(), "monolith running");@@ -92,6 +92,7 @@ impl State {
9292
event_loop: &mut EventLoop<'static, State>,9393
display: Display<State>,9494
config_path: Option<PathBuf>,95
+ gpu: Option<u64>,9596
) -> Result<Self, Box<dyn std::error::Error>> {9697
let dh = display.handle();9798
let config_path = config_path.or_else(Config::path);@@ -99,7 +100,10 @@ impl State {
99100
tracing::error!("config: {e}, using the built-in default");100101
Config::parse(monolith_config::DEFAULT).expect("the default config parses")101102
});102
- let renderer = VulkanRenderer::new()?;103
+ let renderer = match gpu {104
+ Some(node) => VulkanRenderer::for_node(node)?,105
+ None => VulkanRenderer::new()?,106
+ };103107
let mut dmabuf_state = DmabufState::new();104108
let dmabuf_global = dmabuf_state.create_global::<State>(&dh, renderer.dmabuf_formats());105109
let mut seat_state = SeatState::new();@@ -11,7 +11,7 @@ workspace = true
1111
[dependencies]1212
ash = "0.38"1313
glam = { version = "0.33", default-features = false, features = ["std"] }14
-rustix = { version = "1", features = ["event"] }14
+rustix = { version = "1", features = ["event", "fs"] }1515
gpu-allocator = { version = "0.27", default-features = false, features = ["vulkan"] }1616
smithay.workspace = true1717
thiserror = "2"@@ -51,8 +51,9 @@ impl Drop for Device {
5151
5252
impl Shared {5353
/// Opens the first suitable device, or the one whose name contains54
- /// `MONOLITH_VK_DEVICE` when that is set.55
- pub fn new() -> Result<Self, Error> {54
+ /// `MONOLITH_VK_DEVICE` when that is set. With `node`, only the device55
+ /// behind that DRM device number qualifies.56
+ pub fn new(node: Option<u64>) -> Result<Self, Error> {5657
let entry = unsafe { ash::Entry::load()? };5758
let app = vk::ApplicationInfo::default()5859
.application_name(c"monolith")@@ -65,7 +66,7 @@ impl Shared {
6566
)?6667
};6768
let wanted = std::env::var("MONOLITH_VK_DEVICE").ok();68
- let picked = unsafe { pick_physical(&instance, wanted.as_deref()) };69
+ let picked = unsafe { pick_physical(&instance, wanted.as_deref(), node) };6970
let Some((physical, queue_family, name)) = picked else {7071
unsafe { instance.destroy_instance(None) };7172
return Err(Error::NoSuitableDevice);@@ -133,9 +134,24 @@ impl Shared {
133134
}134135
}135136
137
+/// Whether `physical` is the device behind DRM device number `node`, as its138
+/// primary or render node.139
+unsafe fn serves_node(instance: &ash::Instance, physical: vk::PhysicalDevice, node: u64) -> bool {140
+ let mut drm = vk::PhysicalDeviceDrmPropertiesEXT::default();141
+ let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm);142
+ unsafe { instance.get_physical_device_properties2(physical, &mut props) };143
+ let (major, minor) = (144
+ rustix::fs::major(node) as i64,145
+ rustix::fs::minor(node) as i64,146
+ );147
+ (drm.has_primary == vk::TRUE && (drm.primary_major, drm.primary_minor) == (major, minor))148
+ || (drm.has_render == vk::TRUE && (drm.render_major, drm.render_minor) == (major, minor))149
+}150
+136151
unsafe fn pick_physical(137152
instance: &ash::Instance,138153
wanted: Option<&str>,154
+ node: Option<u64>,139155
) -> Option<(vk::PhysicalDevice, u32, String)> {140156
let devices = unsafe { instance.enumerate_physical_devices() }.ok()?;141157
let mut candidates = Vec::new();@@ -158,11 +174,20 @@ unsafe fn pick_physical(
158174
if f13.dynamic_rendering == vk::FALSE || f13.synchronization2 == vk::FALSE {159175
continue;160176
}161
- let has_push = unsafe { instance.enumerate_device_extension_properties(physical) }162
- .unwrap_or_default()163
- .iter()164
- .any(|e| e.extension_name_as_c_str().ok() == Some(ash::khr::push_descriptor::NAME));165
- if !has_push {177
+ let extensions =178
+ unsafe { instance.enumerate_device_extension_properties(physical) }.unwrap_or_default();179
+ let has = |n: &std::ffi::CStr| {180
+ extensions181
+ .iter()182
+ .any(|e| e.extension_name_as_c_str().ok() == Some(n))183
+ };184
+ if !has(ash::khr::push_descriptor::NAME) {185
+ continue;186
+ }187
+ if let Some(node) = node188
+ && !(has(ash::ext::physical_device_drm::NAME)189
+ && unsafe { serves_node(instance, physical, node) })190
+ {166191
continue;167192
}168193
let families = unsafe { instance.get_physical_device_queue_family_properties(physical) };@@ -60,7 +60,17 @@ impl fmt::Debug for VulkanRenderer {
6060
6161
impl VulkanRenderer {6262
pub fn new() -> Result<Self, Error> {63
- let shared = Arc::new(Shared::new()?);63
+ Self::with_node(None)64
+ }65
+66
+ /// A renderer on the GPU behind DRM device number `node`, so it can67
+ /// render into buffers that GPU scans out.68
+ pub fn for_node(node: u64) -> Result<Self, Error> {69
+ Self::with_node(Some(node))70
+ }71
+72
+ fn with_node(node: Option<u64>) -> Result<Self, Error> {73
+ let shared = Arc::new(Shared::new(node)?);6474
let d = &shared.device.device;6575
let pool = unsafe {6676
d.create_command_pool(