use std::fs;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Platform {
Linux,
Windows,
Macos,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GpuVendor {
Amd,
Nvidia,
Intel,
Unknown,
}
pub fn detect_platform() -> Platform {
match std::env::consts::OS {
"windows" => Platform::Windows,
"macos" => Platform::Macos,
_ => Platform::Linux,
}
}
pub fn is_arm64() -> bool {
cfg!(target_arch = "aarch64")
}
pub fn platform_name(platform: Platform) -> &'static str {
match platform {
Platform::Linux => "linux",
Platform::Windows => "windows",
Platform::Macos => "macos",
}
}
pub fn backend_supported(backend: crate::models::Backend, platform: Platform) -> bool {
match platform {
Platform::Linux => backend.is_linux(),
Platform::Windows => backend.is_windows(),
Platform::Macos => backend.is_macos(),
}
}
fn drm_card_paths() -> Vec<std::path::PathBuf> {
let drm_path = Path::new("/sys/class/drm");
if !drm_path.exists() {
return Vec::new();
}
fs::read_dir(drm_path)
.map(|entries| {
entries
.flatten()
.filter(|e| {
let n = e.file_name();
let s = n.to_string_lossy();
s.starts_with("card") && !s.contains('-')
})
.map(|e| e.path())
.collect()
})
.unwrap_or_default()
}
pub fn detect_gpu_vendors() -> Vec<GpuVendor> {
let mut vendors = Vec::new();
for card_path in drm_card_paths() {
let vendor_path = card_path.join("device/vendor");
if let Ok(vendor_id) = fs::read_to_string(vendor_path) {
let vendor_id = vendor_id.trim();
let vendor = match vendor_id {
"0x1002" => GpuVendor::Amd,
"0x10de" => GpuVendor::Nvidia,
"0x8086" => GpuVendor::Intel,
_ => continue,
};
if !vendors.contains(&vendor) {
vendors.push(vendor);
}
}
}
if vendors.is_empty() {
vendors.push(GpuVendor::Unknown);
}
vendors
}
pub fn detect_gpu_models() -> Vec<Option<String>> {
let card_paths = drm_card_paths();
if card_paths.is_empty() {
return Vec::new();
}
let amd_gfx_targets = detect_amd_gfx_targets();
let mut amd_card_idx: usize = 0;
let mut models = Vec::new();
for card_path in &card_paths {
let vendor_path = card_path.join("device/vendor");
if let Ok(vendor_id) = fs::read_to_string(vendor_path) {
let vendor_id = vendor_id.trim();
let vendor = match vendor_id {
"0x1002" => GpuVendor::Amd,
"0x10de" => GpuVendor::Nvidia,
"0x8086" => GpuVendor::Intel,
_ => continue,
};
let vendor_name = match vendor {
GpuVendor::Amd => "AMD",
GpuVendor::Nvidia => "NVIDIA",
GpuVendor::Intel => "Intel",
GpuVendor::Unknown => continue,
};
if vendor == GpuVendor::Amd {
if let Some(gfx) = amd_gfx_targets.get(amd_card_idx % amd_gfx_targets.len()) {
models.push(Some(format!("{} ({})", vendor_name, gfx)));
} else {
models.push(Some(vendor_name.to_string()));
}
amd_card_idx += 1;
} else {
models.push(Some(vendor_name.to_string()));
}
}
}
models
}
fn gfx_target_to_string(val: u32) -> Option<String> {
if val == 0 {
return None;
}
let major = val / 10000;
let minor = (val % 10000) / 100;
let stepping = val % 100;
if stepping > 0 {
Some(format!("gfx{}{}{}", major, minor, stepping))
} else {
Some(format!("gfx{}{}", major, minor))
}
}
pub fn detect_amd_gfx_targets() -> Vec<String> {
let kfd_path = Path::new("/sys/class/kfd/kfd/topology/nodes");
if !kfd_path.exists() {
return Vec::new();
}
let mut targets = Vec::new();
if let Ok(entries) = fs::read_dir(kfd_path) {
for entry in entries.flatten() {
let props_path = entry.path().join("properties");
if let Ok(props) = fs::read_to_string(props_path) {
for line in props.lines() {
if line.starts_with("gfx_target_version")
&& let Some(val_str) = line.split_whitespace().last()
&& let Ok(val) = val_str.parse::<u32>()
&& let Some(gfx) = gfx_target_to_string(val) {
if !targets.contains(&gfx) {
targets.push(gfx);
}
break;
}
}
}
}
}
targets
}
pub fn detect_amd_gfx_target() -> Option<String> {
detect_amd_gfx_targets().into_iter().next()
}
pub fn get_lemonade_gfx_suffix(gfx: &str) -> &'static str {
if gfx.starts_with("gfx103") {
"gfx103X"
} else if gfx.starts_with("gfx110") {
"gfx110X"
} else if gfx == "gfx1150" {
"gfx1150"
} else if gfx == "gfx1151" {
"gfx1151"
} else if gfx.starts_with("gfx120") {
"gfx120X"
} else {
"gfx110X"
}
}