diff --git a/src/gpu_profile.rs b/src/gpu_profile.rs index 622d33a..b1bbdac 100644 --- a/src/gpu_profile.rs +++ b/src/gpu_profile.rs @@ -1,10 +1,14 @@ use crate::file_utils::get_reader; use std::{error::Error, fmt::Display, io::Read, str::FromStr}; -const POW_PROF_PATH: &str = "/sys/class/drm/card0/device/pp_power_profile_mode"; +// const POW_PROF_PATH: &str = "/sys/class/drm/card0/device/pp_power_profile_mode"; -pub fn get_set_vr_pow_prof_cmd() -> String { - format!("sudo sh -c \"echo '4' > {}\"", POW_PROF_PATH) +fn power_profile_mode_file(card_dir: &str) -> String { + format!("{}/device/pp_power_profile_mode", card_dir) +} + +pub fn get_set_amd_vr_pow_prof_cmd(card_dir: &str) -> String { + format!("sudo sh -c \"echo '4' > {}\"", power_profile_mode_file(card_dir)) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -75,13 +79,53 @@ impl FromStr for GpuPowerProfile { } } -pub fn get_gpu_power_profile() -> Option { - if let Some(mut reader) = get_reader(&POW_PROF_PATH.into()) { - let mut txt = String::new(); - reader.read_to_string(&mut txt).ok()?; - for line in txt.split('\n') { - if let Ok(pp) = GpuPowerProfile::from_str(line) { - return Some(pp); +const AMD_VENDOR_ID: &str = "0x1002"; +// TODO: add these other ids +// const INTEL_VENDOR_ID: &str = "0xGGGG"; +// const NVIDIA_VENDOR_ID: &str = "0xGGGG"; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GpuSysDrm { + Amd(String), + Intel(String), + Nvidia(String), + Other(String), +} + +fn list_gpus() -> Vec { + let mut res = vec![]; + + for i in 0..5 { // arbitrary range, find a better way + let card_dir = format!("/sys/class/drm/card{}", i); + let vendor_file = format!("{}/device/vendor", card_dir); + if let Some(mut reader) = get_reader(&vendor_file) { + let mut buf = String::new(); + if reader.read_to_string(&mut buf).is_ok() { + res.push(match buf.to_lowercase().trim() { + AMD_VENDOR_ID => GpuSysDrm::Amd(card_dir), + _ => GpuSysDrm::Other(card_dir), + }); + } + } + } + + res +} + +pub fn get_first_amd_gpu() -> Option { + list_gpus().iter().filter(|g| matches!(g, GpuSysDrm::Amd(_))).next().cloned() +} + +pub fn get_amd_gpu_power_profile() -> Option { + let amd_gpu = get_first_amd_gpu(); + if let Some(GpuSysDrm::Amd(card_dir)) = amd_gpu { + if let Some(mut reader) = get_reader(&power_profile_mode_file(card_dir.as_str())) { + let mut txt = String::new(); + reader.read_to_string(&mut txt).ok()?; + for line in txt.split('\n') { + if let Ok(pp) = GpuPowerProfile::from_str(line) { + return Some(pp); + } } } } diff --git a/src/ui/main_view.rs b/src/ui/main_view.rs index 175cc0f..5722f55 100644 --- a/src/ui/main_view.rs +++ b/src/ui/main_view.rs @@ -6,7 +6,7 @@ use super::steam_launch_options_box::{SteamLaunchOptionsBox, SteamLaunchOptionsB use crate::config::Config; use crate::constants::APP_NAME; use crate::file_utils::mount_has_nosuid; -use crate::gpu_profile::{get_gpu_power_profile, get_set_vr_pow_prof_cmd, GpuPowerProfile}; +use crate::gpu_profile::{get_amd_gpu_power_profile, get_set_amd_vr_pow_prof_cmd, GpuPowerProfile, get_first_amd_gpu, GpuSysDrm}; use crate::profile::{LighthouseDriver, Profile}; use crate::steamvr_utils::chaperone_info_exists; use crate::ui::app::{ @@ -57,6 +57,7 @@ pub enum MainViewMsg { SetSelectedProfile(u32), ProfileSelected(u32), UpdateSelectedProfile(Profile), + TickSelectedProfileTracker, EditProfile, CreateProfile, DeleteProfile, @@ -290,7 +291,11 @@ impl SimpleComponent for MainView { set_margin_bottom: 12, // TODO: maybe add manual refresh btn? #[track = "model.changed(Self::selected_profile())"] - set_visible: get_gpu_power_profile() != Some(GpuPowerProfile::VR), + set_visible: match get_amd_gpu_power_profile() { + None => false, + Some(GpuPowerProfile::VR) => false, + Some(_) => true, + }, gtk::Separator { set_orientation: gtk::Orientation::Horizontal, set_hexpand: true, @@ -313,7 +318,7 @@ impl SimpleComponent for MainView { }, gtk::Label { set_label: concat!( - "Your GPU Power Profile is not set to VR. ", + "Your AMD GPU Power Profile is not set to VR. ", "This will cause noticeable stutter when running XR ", "applications. Activate the VR profile witrh the ", "following command:", @@ -344,7 +349,14 @@ impl SimpleComponent for MainView { set_bottom_margin: 18, #[wrap(Some)] set_buffer: cmdbuf = >k::TextBuffer { - set_text: get_set_vr_pow_prof_cmd().as_str(), + set_text: &{ + let mut res = String::new(); + if let Some(GpuSysDrm::Amd(d)) = get_first_amd_gpu() { + let ds = d.as_str(); + res = get_set_amd_vr_pow_prof_cmd(ds); + } + res + } } } }, @@ -356,13 +368,22 @@ impl SimpleComponent for MainView { set_vexpand: false, set_valign: gtk::Align::Center, connect_clicked => move |_| { - gtk::gdk::Display::default() - .expect("Could not find default display") - .clipboard() - .set_text(get_set_vr_pow_prof_cmd().as_str()); + if let Some(GpuSysDrm::Amd(d)) = get_first_amd_gpu() { + gtk::gdk::Display::default() + .expect("Could not find default display") + .clipboard() + .set_text(get_set_amd_vr_pow_prof_cmd(d.as_str()).as_str()); + } } }, }, + gtk::Button { + set_halign: gtk::Align::Start, + set_label: "Refresh", + connect_clicked[sender] => move |_| { + sender.input(Self::Input::TickSelectedProfileTracker); + } + }, }, model.steam_launch_options_box.widget(), model.install_wivrn_box.widget(), @@ -537,6 +558,9 @@ impl SimpleComponent for MainView { .sender() .emit(InstallWivrnBoxMsg::UpdateSelectedProfile(prof.clone())); } + Self::Input::TickSelectedProfileTracker => { + self.set_selected_profile(self.selected_profile.clone()); + } Self::Input::UpdateProfiles(profiles, config) => { self.set_profiles(profiles); // why send another message to set the dropdown selection?