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

View file

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

View file

@ -19,8 +19,11 @@ use crate::{
paths::{get_data_dir, get_home_dir},
profile::{LighthouseDriver, Profile, XRServiceType},
stateless_action,
util::file_utils::{get_writer, mount_has_nosuid},
util::steamvr_utils::chaperone_info_exists,
util::{
file_utils::{get_writer, mount_has_nosuid},
steamvr_utils::chaperone_info_exists,
},
vulkaninfo::VulkanInfo,
xr_devices::XRDevice,
};
use adw::{prelude::*, ResponseAppearance};
@ -65,6 +68,8 @@ pub struct MainView {
#[tracker::do_not_track]
profile_export_action: gtk::gio::SimpleAction,
xrservice_ready: bool,
#[tracker::do_not_track]
vkinfo: Option<VulkanInfo>,
}
#[derive(Debug)]
@ -104,6 +109,7 @@ pub struct MainViewInit {
pub config: Config,
pub selected_profile: Profile,
pub root_win: gtk::Window,
pub vkinfo: Option<VulkanInfo>,
}
impl MainView {
@ -328,6 +334,34 @@ impl SimpleComponent for MainView {
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 {
set_orientation: gtk::Orientation::Vertical,
set_hexpand: true,
@ -812,6 +846,7 @@ impl SimpleComponent for MainView {
xrservice_ready: false,
profile_delete_action,
profile_export_action,
vkinfo: init.vkinfo,
tracker: 0,
};
let widgets = view_output!();

View file

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