feat: detect nvidia gpu; if detected and monado vk layers not found show warning
Some checks failed
/ cargo-fmtcheck (push) Has been cancelled
/ cargo-clippy (push) Has been cancelled
/ cargo-test (push) Has been cancelled
/ appimage (push) Has been cancelled

This commit is contained in:
Gabriele Musco 2024-09-24 20:17:06 +02:00
commit 4fad4f37d5
No known key found for this signature in database
GPG key ID: 1068D795C80E51DE
4 changed files with 122 additions and 31 deletions

View file

@ -5,7 +5,7 @@ use crate::{
}, },
device_prober::PhysicalXRDevice, device_prober::PhysicalXRDevice,
linux_distro::LinuxDistro, linux_distro::LinuxDistro,
vulkaninfo::gpu_names, vulkaninfo::VulkanInfo,
xdg::XDG, xdg::XDG,
}; };
use relm4::prelude::*; use relm4::prelude::*;
@ -25,7 +25,7 @@ pub fn create_about_dialog() -> adw::AboutDialog {
.build() .build()
} }
pub fn populate_debug_info(dialog: &adw::AboutDialog) { pub fn populate_debug_info(dialog: &adw::AboutDialog, vkinfo: Option<&VulkanInfo>) {
if dialog.debug_info().len() > 0 { if dialog.debug_info().len() > 0 {
return; return;
} }
@ -58,9 +58,14 @@ pub fn populate_debug_info(dialog: &adw::AboutDialog) {
), ),
format!( format!(
"GPUs: {}", "GPUs: {}",
unsafe { gpu_names() } vkinfo
.ok() .map(|i| i.gpu_names.join(", "))
.map(|names| names.join(", ")) .unwrap_or("unknown".into())
),
format!(
"Monado Vulkan Layers: {}",
vkinfo
.map(|i| i.has_monado_vulkan_layers.to_string())
.unwrap_or("unknown".into()) .unwrap_or("unknown".into())
), ),
format!("Detected XR Devices: {}", { format!("Detected XR Devices: {}", {

View file

@ -47,6 +47,7 @@ use crate::{
restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile, restore_runtime_entrypoint, set_runtime_entrypoint_launch_opts_from_profile,
}, },
util::file_utils::{setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd}, util::file_utils::{setcap_cap_sys_nice_eip, setcap_cap_sys_nice_eip_cmd},
vulkaninfo::VulkanInfo,
xr_devices::XRDevice, xr_devices::XRDevice,
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
@ -86,6 +87,7 @@ pub struct App {
configure_wivrn_action: gtk::gio::SimpleAction, configure_wivrn_action: gtk::gio::SimpleAction,
openxr_prober_worker: Option<JobWorker>, openxr_prober_worker: Option<JobWorker>,
xrservice_ready: bool, xrservice_ready: bool,
vkinfo: Option<VulkanInfo>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -861,6 +863,16 @@ impl AsyncComponent for App {
}; };
let selected_profile = config.get_selected_profile(&profiles); let selected_profile = config.get_selected_profile(&profiles);
let vkinfo = {
match VulkanInfo::get() {
Ok(info) => Some(info),
Err(e) => {
eprintln!("Failed to get Vulkan info: {e:#?}");
None
}
}
};
let mut model = App { let mut model = App {
application: init.application, application: init.application,
app_win: root.clone(), app_win: root.clone(),
@ -870,6 +882,7 @@ impl AsyncComponent for App {
config: config.clone(), config: config.clone(),
selected_profile: selected_profile.clone(), selected_profile: selected_profile.clone(),
root_win: root.clone().into(), root_win: root.clone().into(),
vkinfo: vkinfo.clone(),
}) })
.forward(sender.input_sender(), |message| match message { .forward(sender.input_sender(), |message| match message {
MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService, MainViewOutMsg::DoStartStopXRService => Msg::DoStartStopXRService,
@ -879,6 +892,7 @@ impl AsyncComponent for App {
MainViewOutMsg::SaveProfile(p) => Msg::SaveProfile(p), MainViewOutMsg::SaveProfile(p) => Msg::SaveProfile(p),
MainViewOutMsg::OpenLibsurviveSetup => Msg::OpenLibsurviveSetup, MainViewOutMsg::OpenLibsurviveSetup => Msg::OpenLibsurviveSetup,
}), }),
vkinfo,
debug_view: DebugView::builder() debug_view: DebugView::builder()
.launch(DebugViewInit { .launch(DebugViewInit {
profile: selected_profile, profile: selected_profile,
@ -926,8 +940,10 @@ impl AsyncComponent for App {
model.about_dialog, model.about_dialog,
#[strong(rename_to = app_win)] #[strong(rename_to = app_win)]
model.app_win, model.app_win,
#[strong(rename_to = vkinfo)]
model.vkinfo,
move |_| { move |_| {
populate_debug_info(&about_dialog); populate_debug_info(&about_dialog, vkinfo.as_ref());
about_dialog.present(Some(&app_win)); about_dialog.present(Some(&app_win));
} }
) )

View file

@ -19,8 +19,11 @@ use crate::{
paths::{get_data_dir, get_home_dir}, paths::{get_data_dir, get_home_dir},
profile::{LighthouseDriver, Profile, XRServiceType}, profile::{LighthouseDriver, Profile, XRServiceType},
stateless_action, stateless_action,
util::file_utils::{get_writer, mount_has_nosuid}, util::{
util::steamvr_utils::chaperone_info_exists, file_utils::{get_writer, mount_has_nosuid},
steamvr_utils::chaperone_info_exists,
},
vulkaninfo::VulkanInfo,
xr_devices::XRDevice, xr_devices::XRDevice,
}; };
use adw::{prelude::*, ResponseAppearance}; use adw::{prelude::*, ResponseAppearance};
@ -65,6 +68,8 @@ pub struct MainView {
#[tracker::do_not_track] #[tracker::do_not_track]
profile_export_action: gtk::gio::SimpleAction, profile_export_action: gtk::gio::SimpleAction,
xrservice_ready: bool, xrservice_ready: bool,
#[tracker::do_not_track]
vkinfo: Option<VulkanInfo>,
} }
#[derive(Debug)] #[derive(Debug)]
@ -104,6 +109,7 @@ pub struct MainViewInit {
pub config: Config, pub config: Config,
pub selected_profile: Profile, pub selected_profile: Profile,
pub root_win: gtk::Window, pub root_win: gtk::Window,
pub vkinfo: Option<VulkanInfo>,
} }
impl MainView { impl MainView {
@ -328,6 +334,34 @@ impl SimpleComponent for MainView {
set_wrap_mode: gtk::pango::WrapMode::Word, set_wrap_mode: gtk::pango::WrapMode::Word,
} }
}, },
gtk::Box {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
set_vexpand: false,
set_spacing: 12,
add_css_class: "card",
add_css_class: "padded",
set_visible: model
.vkinfo
.as_ref()
.is_some_and(
|i| i.has_nvidia_gpu && !i.has_monado_vulkan_layers
),
warning_heading(),
gtk::Label {
set_label: concat!(
"An Nvidia GPU has been detected, but it ",
"seems you don't have the Monado Vulkan Layers ",
"installed on your system.\n\nInstall the ",
"Monado Vulkan Layers or your XR session will ",
"crash."
),
add_css_class: "warning",
set_xalign: 0.0,
set_wrap: true,
set_wrap_mode: gtk::pango::WrapMode::Word,
}
},
gtk::Box { gtk::Box {
set_orientation: gtk::Orientation::Vertical, set_orientation: gtk::Orientation::Vertical,
set_hexpand: true, set_hexpand: true,
@ -812,6 +846,7 @@ impl SimpleComponent for MainView {
xrservice_ready: false, xrservice_ready: false,
profile_delete_action, profile_delete_action,
profile_export_action, profile_export_action,
vkinfo: init.vkinfo,
tracker: 0, tracker: 0,
}; };
let widgets = view_output!(); let widgets = view_output!();

View file

@ -3,27 +3,62 @@ use ash::{
Entry, Entry,
}; };
/// # Safety #[derive(Debug, Clone)]
/// pub struct VulkanInfo {
/// Dlopens the vulkan library, so this is inherently unsafe. Should be fine in pub has_nvidia_gpu: bool,
/// most circumstances pub has_monado_vulkan_layers: bool,
pub unsafe fn gpu_names() -> anyhow::Result<Vec<String>> { pub gpu_names: Vec<String>,
let entry = Entry::load()?; }
let instance = entry.create_instance(
&InstanceCreateInfo::default().application_info(&ApplicationInfo::default()), const NVIDIA_VENDOR_ID: u32 = 0x10de;
None,
)?; impl VulkanInfo {
let names = instance /// # Safety
.enumerate_physical_devices()? ///
.into_iter() /// Dlopens the vulkan library, so this is inherently unsafe. Should be fine in
.filter_map(|d| { /// most circumstances
instance pub fn get() -> anyhow::Result<Self> {
.get_physical_device_properties(d) let entry = unsafe { Entry::load() }?;
.device_name_as_c_str() let instance = unsafe {
.ok() entry.create_instance(
.map(|cs| cs.to_string_lossy().to_string()) &InstanceCreateInfo::default().application_info(&ApplicationInfo::default()),
}) None,
.collect(); )
instance.destroy_instance(None); }?;
Ok(names) let mut has_nvidia_gpu = false;
let mut has_monado_vulkan_layers = false;
let gpu_names = unsafe { instance.enumerate_physical_devices() }?
.into_iter()
.filter_map(|d| {
let props = unsafe { instance.get_physical_device_properties(d) };
if props.vendor_id == NVIDIA_VENDOR_ID {
has_nvidia_gpu = true;
}
if !has_monado_vulkan_layers {
has_monado_vulkan_layers =
unsafe { instance.enumerate_device_layer_properties(d) }
.ok()
.map(|layerprops| {
layerprops.iter().any(|lp| {
lp.layer_name_as_c_str().is_ok_and(|name| {
name.to_string_lossy()
== "VK_LAYER_MND_enable_timeline_semaphore"
})
})
})
== Some(true);
}
props
.device_name_as_c_str()
.ok()
.map(|cs| cs.to_string_lossy().to_string())
})
.collect();
unsafe { instance.destroy_instance(None) };
Ok(Self {
gpu_names,
has_nvidia_gpu,
has_monado_vulkan_layers,
})
}
} }