diff --git a/Cargo.lock b/Cargo.lock index 32705dc..3958b60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1242,6 +1242,8 @@ version = "0.1.0" dependencies = [ "aliasable", "anyhow", + "ash", + "bytemuck", "calloop 0.14.3", "calloop-wayland-source 0.4.1", "clap", diff --git a/Cargo.toml b/Cargo.toml index 5561773..25b4ec3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,8 @@ aliasable = "0.1.3" futures-executor = { version = "0.3.31", features = ["thread-pool"] } zbus = "5.9.0" tokio-stream = { version = "0.1.17", features = ["sync"] } +ash = { version = "0.38.0", features = ["loaded"] } +bytemuck = "1.23.2" [dependencies.i18n-embed] version = "0.16" diff --git a/src/backend/wayland/buffer.rs b/src/backend/wayland/buffer.rs index ce91d51..f05dd0d 100644 --- a/src/backend/wayland/buffer.rs +++ b/src/backend/wayland/buffer.rs @@ -93,6 +93,14 @@ impl AppData { return Ok(None); }; let drm_dev = drm_dev.unwrap_or(feedback.main_device() as u64); + if let Some(vulkan) = &mut self.vulkan { + if let Ok(Some(name)) = vulkan.device_name(drm_dev) { + // TODO Workaround: force shm on Meteor/Arrow/Lunar Lake + if name.contains("MTL") || name.contains("ARL") || name.contains("LNL") { + return Ok(None); + } + } + } let Some((_dev_path, gbm)) = self.gbm_devices.gbm_device(drm_dev)? else { return Ok(None); }; diff --git a/src/backend/wayland/mod.rs b/src/backend/wayland/mod.rs index 2ca7285..654682c 100644 --- a/src/backend/wayland/mod.rs +++ b/src/backend/wayland/mod.rs @@ -39,6 +39,7 @@ use gbm_devices::GbmDevices; mod screencopy; use screencopy::{ScreencopySession, SessionData}; mod toplevel; +mod vulkan; mod workspace; use super::{CaptureFilter, CaptureImage, Cmd, Event}; @@ -63,6 +64,7 @@ pub struct AppData { dmabuf_feedback: Option, gbm_devices: GbmDevices, thread_pool: futures_executor::ThreadPool, + vulkan: Option, } impl AppData { @@ -308,6 +310,7 @@ fn start(conn: Connection) -> mpsc::Receiver { dmabuf_feedback: None, gbm_devices: GbmDevices::default(), thread_pool, + vulkan: vulkan::Vulkan::new(), }; let (cmd_sender, cmd_channel) = calloop::channel::channel(); diff --git a/src/backend/wayland/vulkan.rs b/src/backend/wayland/vulkan.rs new file mode 100644 index 0000000..11b4c7c --- /dev/null +++ b/src/backend/wayland/vulkan.rs @@ -0,0 +1,79 @@ +use ash::{prelude::VkResult, vk}; +use std::{collections::HashMap, ffi::CStr}; + +pub struct Vulkan { + instance: ash::Instance, + // TODO purge cache at some point + device_name_cache: HashMap>>, +} + +impl Vulkan { + pub fn new() -> Option { + let entry = unsafe { ash::Entry::load().ok()? }; + let app_info = vk::ApplicationInfo { + api_version: vk::make_api_version(0, 1, 1, 0), + ..Default::default() + }; + let create_info = vk::InstanceCreateInfo { + p_application_info: &app_info, + ..Default::default() + }; + let instance = unsafe { entry.create_instance(&create_info, None).ok()? }; + Some(Self { + instance, + device_name_cache: HashMap::new(), + }) + } + + pub fn device_name(&mut self, dev: u64) -> VkResult> { + if !self.device_name_cache.contains_key(&dev) { + let value = self.device_name_uncached(dev); + self.device_name_cache.insert(dev, value); + } + self.device_name_cache + .get(&dev) + .unwrap() + .as_ref() + .map(|x| x.as_deref()) + .map_err(|err| err.clone()) + } + + fn device_name_uncached(&mut self, dev: u64) -> VkResult> { + let devices = unsafe { self.instance.enumerate_physical_devices()? }; + for device in devices { + // Check extension is supported + let supported = unsafe { + self.instance + .enumerate_device_extension_properties(device)? + }; + if !supported.iter().any(|ext| { + CStr::from_bytes_until_nul(bytemuck::cast_slice(&ext.extension_name)) + == Ok(ash::ext::physical_device_drm::NAME) + }) { + continue; + } + + let mut drm_props = vk::PhysicalDeviceDrmPropertiesEXT::default(); + let mut props = vk::PhysicalDeviceProperties2::default().push_next(&mut drm_props); + unsafe { + self.instance + .get_physical_device_properties2(device, &mut props) + }; + + let device_name = + CStr::from_bytes_until_nul(bytemuck::cast_slice(&props.properties.device_name)); + + let major = rustix::fs::major(dev) as _; + let minor = rustix::fs::minor(dev) as _; + if (drm_props.primary_major, drm_props.primary_minor) == (major, minor) + || (drm_props.render_major, drm_props.render_minor) == (major, minor) + { + return Ok(device_name + .ok() + .and_then(|x| Some(x.to_str().ok()?.to_owned()))); + } + } + + Ok(None) + } +}