pub const INTEGRATED_PCI_DEVICE_IDS: &[&str] = &[
"0x164e", "0x15c8", "0x15e0", "0x1681", "0x1506", "0x1638", "0x15d8", "0x1640", ];
pub const DISCRETE_MIN_VRAM_BYTES: u64 = 4 * 1024 * 1024 * 1024;
pub const INTEGRATED_GFX_ARCHS: &[&str] = &[
"gfx1036", "gfx1103", ];
pub fn is_integrated_by_gfx_arch(gfx: &str) -> bool {
let gfx = gfx.trim().to_ascii_lowercase();
INTEGRATED_GFX_ARCHS
.iter()
.any(|a| a.eq_ignore_ascii_case(&gfx))
}
pub const INTEGRATED_NAME_PATTERNS: &[&str] = &[
"Cezanne",
"Rembrandt",
"Phoenix",
"Raphael",
"Barcelo",
"Pink Sardine",
"Yellow Carp",
"Green Sardine",
"Hawk Point",
"Dragon Range",
"Fire Range",
"Stella",
"Krackan",
"Strix Point",
"Mendocino",
"Raven",
"Picasso",
"Renoir",
"Integrated",
"iGPU",
"APU",
"AMD Radeon Graphics",
"Radeon Graphics",
" 5600G",
" 5700G",
" 5600GE",
" 5700GE",
" 5700G",
" 5600GT",
" 5700GT",
" 8600G",
" 8700G",
" 8500G",
" 8300G",
"7800X3D",
"7950X3D",
"7900X3D",
"7700X3D",
"7600X3D",
"Ryzen 7 7735",
"Ryzen 7 7840",
"Ryzen 7 8840",
"Ryzen 5 7535",
"Ryzen 5 7640",
"Ryzen 5 8640",
"Ryzen 9 7945",
"Ryzen 9 8945",
];
fn integrated_name_patterns_lower() -> &'static [String] {
use std::sync::OnceLock;
static LOWER: OnceLock<Vec<String>> = OnceLock::new();
LOWER.get_or_init(|| {
INTEGRATED_NAME_PATTERNS
.iter()
.map(|p| p.to_lowercase())
.collect()
})
}
pub fn is_integrated_gpu_name(marketing_name: &str) -> bool {
let name_lower = marketing_name.to_lowercase();
let name_upper = marketing_name.to_uppercase();
for pattern in integrated_name_patterns_lower() {
if name_lower.contains(pattern) {
return true;
}
}
if name_upper.contains("RYZEN") && !name_upper.contains("RX") {
return true;
}
if let Some(pos) = name_upper.find("RYZEN") {
let after_ryzen = &name_upper[pos + 5..];
for word in after_ryzen.split_whitespace() {
if (word.ends_with('G') || word.ends_with("GE") || word.ends_with("GT"))
&& word.len() >= 4
&& !word.starts_with("RX")
&& !word.starts_with("RADEON")
&& !word.contains("XT")
&& !word.contains("XTX")
{
return true;
}
}
}
if name_upper.contains("RADEON GRAPHICS")
&& !name_upper.contains("RX")
&& (!marketing_name.chars().any(|c| c.is_ascii_digit())
|| marketing_name.contains("AMD Radeon Graphics"))
{
return true;
}
false
}
pub fn is_integrated_by_pci_id(pci_device_id: &str) -> bool {
let normalized = pci_device_id.trim().to_ascii_lowercase();
let normalized = normalized.trim_start_matches("0x");
INTEGRATED_PCI_DEVICE_IDS
.iter()
.any(|id| id.trim_start_matches("0x").eq_ignore_ascii_case(normalized))
}
pub fn device_is_integrated(
name: Option<&str>,
pci_device_id: Option<&str>,
gfx_arch: Option<&str>,
vram_bytes: Option<u64>,
) -> bool {
if let Some(id) = pci_device_id {
if is_integrated_by_pci_id(id) {
return true;
}
}
if let Some(g) = gfx_arch {
if !g.trim().is_empty() && is_integrated_by_gfx_arch(g) {
return true;
}
}
if let Some(name) = name {
if !name.trim().is_empty() && is_integrated_gpu_name(name) {
return true;
}
if let Some(vram) = vram_bytes {
let upper = name.to_ascii_uppercase();
let has_discrete_marker = upper.contains("RADEON RX")
|| upper.contains(" RX ")
|| upper.contains("RADEON PRO")
|| upper.contains("INSTINCT")
|| upper.contains("FIREPRO");
if !name.trim().is_empty() && !has_discrete_marker && vram < DISCRETE_MIN_VRAM_BYTES {
return true;
}
}
}
false
}
pub fn read_vram_bytes(pci_slot: &str) -> Option<u64> {
let slot = pci_slot.trim().trim_start_matches("0000:");
let path = format!("/sys/bus/pci/devices/0000:{slot}/mem_info_vram_total");
let value = std::fs::read_to_string(&path).ok()?;
value.trim().parse::<u64>().ok()
}
#[derive(Debug, Clone)]
pub struct AmdGpu {
pub pci_slot: String,
pub pci_device_id: String,
pub marketing_name: Option<String>,
pub gfx_arch: Option<String>,
pub vram_bytes: Option<u64>,
pub is_integrated: bool,
pub rocm_index: Option<u32>,
}
pub const DISCRETE_PCI_ID_TO_GFX: &[(&str, &str)] = &[
("0x7550", "gfx1201"),
("0x7551", "gfx1201"),
("0x7590", "gfx1200"),
("0x744c", "gfx1100"),
("0x7448", "gfx1100"),
("0x7449", "gfx1100"),
("0x744a", "gfx1100"),
("0x744b", "gfx1100"),
("0x745e", "gfx1100"),
("0x747e", "gfx1101"),
("0x7470", "gfx1101"),
("0x7460", "gfx1101"),
("0x7461", "gfx1101"),
("0x7480", "gfx1102"),
("0x7483", "gfx1102"),
("0x7489", "gfx1102"),
("0x749f", "gfx1102"),
("0x73f0", "gfx1102"),
("0x73bf", "gfx1030"),
("0x73af", "gfx1030"),
("0x73a5", "gfx1030"),
("0x73a1", "gfx1030"),
("0x73a2", "gfx1030"),
("0x73a3", "gfx1030"),
("0x73df", "gfx1031"),
("0x73c3", "gfx1031"),
("0x73ff", "gfx1032"),
("0x73ef", "gfx1032"),
("0x73e0", "gfx1032"),
("0x73e1", "gfx1032"),
("0x73e3", "gfx1032"),
("0x743f", "gfx1034"),
("0x7424", "gfx1034"),
("0x7421", "gfx1034"),
("0x7422", "gfx1034"),
("0x7423", "gfx1034"),
];
fn normalize_pci_id(id: &str) -> String {
id.trim()
.to_ascii_lowercase()
.trim_start_matches("0x")
.to_string()
}
pub fn pci_id_to_gfx(pci_device_id: &str) -> Option<&'static str> {
let normalized = normalize_pci_id(pci_device_id);
DISCRETE_PCI_ID_TO_GFX
.iter()
.find(|(id, _)| normalize_pci_id(id) == normalized)
.map(|(_, gfx)| *gfx)
}
pub fn gfx_from_marketing_name(name: &str) -> Option<&'static str> {
let name_lower = name.to_lowercase();
if name_lower.contains("navi 48") || name_lower.contains("navi48") {
return Some("gfx1201");
}
if name_lower.contains("navi 44") || name_lower.contains("navi44") {
return Some("gfx1200");
}
if name_lower.contains("navi 31") || name_lower.contains("navi31") {
return Some("gfx1100");
}
if name_lower.contains("navi 32") || name_lower.contains("navi32") {
return Some("gfx1101");
}
if name_lower.contains("navi 33") || name_lower.contains("navi33") {
return Some("gfx1102");
}
if name_lower.contains("navi 21") || name_lower.contains("navi21") {
return Some("gfx1030");
}
if name_lower.contains("navi 22") || name_lower.contains("navi22") {
return Some("gfx1031");
}
if name_lower.contains("navi 23") || name_lower.contains("navi23") {
return Some("gfx1032");
}
if name_lower.contains("navi 24") || name_lower.contains("navi24") {
return Some("gfx1034");
}
if name_lower.contains("7900 xtx") || name_lower.contains("7900xtx") {
return Some("gfx1100");
}
if name_lower.contains("7900 gre") || name_lower.contains("7900gre") {
return Some("gfx1100");
}
if name_lower.contains("7900 xt") || name_lower.contains("7900xt") {
return Some("gfx1100");
}
if name_lower.contains("7800 xt") || name_lower.contains("7800xt") {
return Some("gfx1101");
}
if name_lower.contains("7800 gre") || name_lower.contains("7800gre") {
return Some("gfx1101");
}
if name_lower.contains("7700 xt") || name_lower.contains("7700xt") {
return Some("gfx1101");
}
if name_lower.contains("7600 xt")
|| name_lower.contains("7600xt")
|| name_lower.contains("7600")
{
return Some("gfx1102");
}
if name_lower.contains("9070 xt")
|| name_lower.contains("9070xt")
|| name_lower.contains("9070 gre")
|| name_lower.contains("9070gre")
{
return Some("gfx1201");
}
if name_lower.contains("9060") {
return Some("gfx1200");
}
if name_lower.contains("6950 xt")
|| name_lower.contains("6950xt")
|| name_lower.contains("6900 xt")
|| name_lower.contains("6900xt")
{
return Some("gfx1030");
}
if name_lower.contains("6800 xt")
|| name_lower.contains("6800xt")
|| name_lower.contains("6800")
|| name_lower.contains("6900")
{
return Some("gfx1030");
}
if name_lower.contains("6700 xt")
|| name_lower.contains("6700xt")
|| name_lower.contains("6750 xt")
|| name_lower.contains("6750xt")
{
return Some("gfx1031");
}
if name_lower.contains("6600 xt")
|| name_lower.contains("6600xt")
|| name_lower.contains("6600")
|| name_lower.contains("6650")
{
return Some("gfx1032");
}
if name_lower.contains("6500 xt") || name_lower.contains("6500xt") {
return Some("gfx1034");
}
None
}
#[derive(Debug, Clone)]
struct LspciDevice {
pci_slot: String,
name: String,
pci_id: String,
}
fn parse_lspci_amd() -> Vec<LspciDevice> {
let output = match std::process::Command::new("lspci").arg("-nn").output() {
Ok(o) if o.status.success() => o.stdout,
_ => return Vec::new(),
};
let mut devices = Vec::new();
let text = String::from_utf8_lossy(&output);
for line in text.lines() {
let line = line.trim();
if !line.contains("AMD") && !line.contains("Advanced Micro Devices") {
continue;
}
if !line.contains("[0300]") && !line.contains("[0302]") {
continue;
}
let pci_slot = line
.split_whitespace()
.next()
.unwrap_or("")
.trim_start_matches("0000:")
.to_string();
if pci_slot.is_empty() {
continue;
}
let pci_id = if let Some(bracket_start) = line.rfind('[') {
let bracket_content = &line[bracket_start + 1..];
let bracket_end = bracket_content.find(']').unwrap_or(bracket_content.len());
let id_str = &bracket_content[..bracket_end];
id_str
.split(':')
.nth(1)
.map(|id| format!("0x{}", id.to_lowercase()))
.unwrap_or_else(|| "unknown".to_string())
} else {
"unknown".to_string()
};
let name = if let Some(class_end) = line.find("]:") {
let after_class = &line[class_end + 2..];
if let Some(dev_bracket) = after_class.rfind('[') {
after_class[..dev_bracket].trim().to_string()
} else {
"AMD GPU".to_string()
}
} else {
"AMD GPU".to_string()
};
devices.push(LspciDevice {
pci_slot,
name,
pci_id,
});
}
devices
}
fn rocminfo_available() -> bool {
std::process::Command::new("rocminfo")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
fn parse_rocminfo_agents() -> Vec<(u32, String, Option<String>)> {
let output = match std::process::Command::new("rocminfo").output() {
Ok(o) if o.status.success() => o.stdout,
_ => return Vec::new(),
};
let mut agents = Vec::new();
let text = String::from_utf8_lossy(&output);
for agent_block in text.split("****") {
let mut name = None;
let mut gfx_arch = None;
let mut index: Option<u32> = None;
for line in agent_block.lines() {
let line = line.trim();
if line.starts_with("Agent ") && index.is_none() {
if let Some(rest) = line.strip_prefix("Agent ") {
if let Ok(n) = rest.trim().parse::<u32>() {
index = Some(n);
}
}
}
if let Some(rest) = line.strip_prefix("Name:") {
name = Some(rest.trim().to_string());
}
if let Some(rest) = line.strip_prefix("gfx version:") {
gfx_arch = Some(rest.trim().to_string());
}
}
if let Some(n) = name {
let idx = index.unwrap_or(agents.len() as u32);
agents.push((idx, n, gfx_arch));
}
}
agents
}
pub fn detect_amd_gpus() -> Vec<AmdGpu> {
use std::collections::{HashMap, HashSet};
let mut gpus = Vec::new();
let mut seen_slots = HashSet::new();
let lspci_devices = parse_lspci_amd();
let mut lspci_map: HashMap<String, LspciDevice> = HashMap::new();
for dev in lspci_devices {
lspci_map.insert(dev.pci_slot.clone(), dev);
}
let entries = match std::fs::read_dir("/sys/class/drm") {
Ok(e) => e,
Err(_) => return gpus,
};
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if !name.starts_with("card") || name.contains('-') {
continue;
}
let device_path = entry.path().join("device");
let canonical = match device_path.canonicalize() {
Ok(p) => p,
Err(_) => continue,
};
if !seen_slots.insert(canonical.clone()) {
continue;
}
let vendor_path = canonical.join("vendor");
let vendor = match std::fs::read_to_string(&vendor_path) {
Ok(v) => v.trim().to_lowercase(),
Err(_) => continue,
};
if !vendor.starts_with("0x1002") {
continue;
}
let class_path = canonical.join("class");
let class = match std::fs::read_to_string(&class_path) {
Ok(c) => c.trim().to_lowercase(),
Err(_) => continue,
};
let class_val = match class.strip_prefix("0x") {
Some(hex) => match u32::from_str_radix(hex, 16) {
Ok(v) => v,
Err(_) => continue,
},
None => continue,
};
const PCI_CLASS_VGA: u32 = 0x030000;
const PCI_CLASS_3D: u32 = 0x030200;
const CLASS_MASK: u32 = 0xFFFF00;
let masked_class = class_val & CLASS_MASK;
if masked_class != PCI_CLASS_VGA && masked_class != PCI_CLASS_3D {
continue;
}
let device_path_id = canonical.join("device");
let sysfs_pci_id = match std::fs::read_to_string(&device_path_id) {
Ok(d) => d.trim().to_string(),
Err(_) => continue,
};
let uevent_path = canonical.join("uevent");
let pci_slot = match std::fs::read_to_string(&uevent_path) {
Ok(content) => content
.lines()
.find(|line| line.starts_with("PCI_SLOT_NAME="))
.and_then(|line| line.split('=').nth(1))
.map(|s| s.trim_start_matches("0000:").to_string())
.unwrap_or_else(|| "unknown".to_string()),
Err(_) => "unknown".to_string(),
};
let vram_bytes = read_vram_bytes(&pci_slot);
let lspci_dev = lspci_map.get(&pci_slot);
let marketing_name = lspci_dev
.as_ref()
.map(|d| d.name.clone())
.filter(|n| !n.is_empty());
let lspci_pci_id = lspci_dev
.as_ref()
.map(|d| d.pci_id.clone())
.filter(|i| i != "unknown");
let pci_device_id = lspci_pci_id.unwrap_or_else(|| sysfs_pci_id.clone());
let mut gfx_arch = pci_id_to_gfx(&pci_device_id).map(|s| s.to_string());
if gfx_arch.is_none() {
if let Some(ref name) = marketing_name {
gfx_arch = gfx_from_marketing_name(name).map(|s| s.to_string());
}
}
let is_integrated = device_is_integrated(
marketing_name.as_deref(),
Some(&pci_device_id),
gfx_arch.as_deref(),
vram_bytes,
);
gpus.push(AmdGpu {
pci_slot,
pci_device_id,
marketing_name,
gfx_arch,
vram_bytes,
is_integrated,
rocm_index: None,
});
}
if rocminfo_available() {
let rocminfo_agents = parse_rocminfo_agents();
if !rocminfo_agents.is_empty() {
for (rocm_idx, rocm_name, rocm_gfx) in rocminfo_agents {
for gpu in &mut gpus {
if let Some(ref name) = gpu.marketing_name {
if name.contains(&rocm_name) || rocm_name.contains(name) {
if gpu.rocm_index.is_none() {
gpu.rocm_index = Some(rocm_idx);
}
if let Some(ref rg) = rocm_gfx {
gpu.gfx_arch = Some(rg.clone());
}
}
}
}
}
}
}
gpus
}
pub fn detect_discrete_amd_gpus() -> Vec<AmdGpu> {
let mut dgpus = detect_amd_gpus()
.into_iter()
.filter(|gpu| !gpu.is_integrated)
.collect::<Vec<_>>();
dgpus.sort_by(|a, b| a.pci_slot.cmp(&b.pci_slot));
dgpus
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn discrete_cards_are_not_integrated() {
assert!(!is_integrated_gpu_name("AMD Radeon RX 7900 XTX"));
assert!(!is_integrated_gpu_name("Radeon RX 7800 XT"));
assert!(!is_integrated_gpu_name("AMD Radeon RX 6900 XT"));
assert!(!is_integrated_gpu_name("AMD Radeon RX 9070 XT"));
}
#[test]
fn apu_codenames_are_integrated() {
assert!(is_integrated_gpu_name("Raphael"));
assert!(is_integrated_gpu_name("raphael")); assert!(is_integrated_gpu_name("RAPHAEL"));
assert!(!is_integrated_gpu_name("gfx1036")); assert!(is_integrated_gpu_name("AMD Cezanne (Radeon Graphics)"));
assert!(is_integrated_gpu_name("Phoenix"));
assert!(is_integrated_gpu_name("Rembrandt"));
assert!(is_integrated_gpu_name("Strix Point")); }
#[test]
fn asus_strix_dgpu_is_not_integrated() {
assert!(!is_integrated_gpu_name("ASUS ROG Strix Radeon RX 7900 XTX"));
assert!(!is_integrated_gpu_name("ROG Strix LC Radeon RX 7800 XT"));
}
#[test]
fn ryzen_cpus_with_igpu_are_integrated() {
assert!(is_integrated_gpu_name(
"AMD Ryzen 7 7800X3D 8-Core Processor"
));
assert!(is_integrated_gpu_name(
"AMD Ryzen 9 7950X3D 16-Core Processor"
));
assert!(is_integrated_gpu_name("AMD Ryzen 5 8600G 6-Core Processor"));
assert!(is_integrated_gpu_name("AMD Ryzen 7 8700G"));
}
#[test]
fn ryzen_apu_suffix_without_amd_prefix_is_integrated() {
assert!(is_integrated_gpu_name("Ryzen 5 5600G"));
assert!(is_integrated_gpu_name("Ryzen 7 5700G"));
assert!(is_integrated_gpu_name("Ryzen 5 5600GE"));
assert!(is_integrated_gpu_name("Ryzen 5 5600GT"));
assert!(is_integrated_gpu_name("Ryzen 7 8700G"));
}
#[test]
fn low_vram_discrete_cards_are_not_vram_rejected() {
let low = 2 * 1024 * 1024 * 1024u64;
assert!(!device_is_integrated(
Some("AMD Radeon RX 550"),
None,
None,
Some(low)
));
assert!(!device_is_integrated(
Some("AMD Radeon Pro WX 2100"),
None,
None,
Some(low)
));
assert!(!device_is_integrated(
Some("AMD Instinct MI50"),
None,
None,
Some(low)
));
assert!(device_is_integrated(
Some("AMD Device"),
Some("0x9999"),
None,
Some(512 * 1024 * 1024)
));
}
#[test]
fn bare_radeon_marketing_name_is_vram_gated() {
let low = 2 * 1024 * 1024 * 1024u64;
assert!(device_is_integrated(Some("Radeon"), None, None, Some(low)));
assert!(device_is_integrated(
Some("AMD Radeon"),
None,
None,
Some(low)
));
assert!(device_is_integrated(
Some("AMD Radeon 780M"),
None,
None,
Some(low)
));
assert!(device_is_integrated(
Some("AMD Radeon 680M"),
None,
None,
Some(low)
));
let high = 16 * 1024 * 1024 * 1024u64;
assert!(!device_is_integrated(
Some("Radeon"),
None,
None,
Some(high)
));
assert!(!device_is_integrated(
Some("AMD Radeon RX 7900 XTX"),
None,
None,
Some(low)
));
}
#[test]
fn generic_radeon_graphics_is_integrated() {
assert!(is_integrated_gpu_name("AMD Radeon Graphics"));
assert!(!is_integrated_gpu_name("AMD Radeon RX Graphics 7900"));
}
#[test]
fn pci_id_denylist_catches_raphael() {
assert!(is_integrated_by_pci_id("0x164e"));
assert!(is_integrated_by_pci_id("164E"));
assert!(is_integrated_by_pci_id("0x164E"));
assert!(!is_integrated_by_pci_id("0x744c")); assert!(!is_integrated_by_pci_id("0x747e")); }
#[test]
fn device_is_integrated_combines_signals() {
assert!(device_is_integrated(Some("Raphael"), None, None, None));
assert!(device_is_integrated(None, Some("0x164e"), None, None));
assert!(device_is_integrated(None, None, Some("gfx1036"), None));
assert!(device_is_integrated(None, None, Some("gfx1103"), None));
assert!(!device_is_integrated(
Some("AMD Radeon RX 7900 XTX"),
Some("0x744c"),
Some("gfx1100"),
Some(24 * 1024 * 1024 * 1024)
));
assert!(device_is_integrated(
Some("AMD Device"),
Some("0x9999"),
None,
Some(512 * 1024 * 1024)
));
assert!(!device_is_integrated(
Some("AMD Device"),
Some("0x9999"),
None,
None
));
}
#[test]
fn gfx_arch_signal_is_safe_for_dgpus() {
assert!(is_integrated_by_gfx_arch("gfx1036"));
assert!(is_integrated_by_gfx_arch("GFX1103"));
assert!(!is_integrated_by_gfx_arch("gfx1100"));
assert!(!is_integrated_by_gfx_arch("gfx1101"));
assert!(!is_integrated_by_gfx_arch("gfx1030"));
}
#[test]
fn pci_id_to_gfx_maps_all_table_entries() {
assert_eq!(pci_id_to_gfx("0x7550"), Some("gfx1201"));
assert_eq!(pci_id_to_gfx("0x7551"), Some("gfx1201"));
assert_eq!(pci_id_to_gfx("0x7590"), Some("gfx1200"));
assert_eq!(pci_id_to_gfx("0x744c"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("0x7448"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("0x745e"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("0x747e"), Some("gfx1101"));
assert_eq!(pci_id_to_gfx("0x7460"), Some("gfx1101"));
assert_eq!(pci_id_to_gfx("0x7480"), Some("gfx1102"));
assert_eq!(pci_id_to_gfx("0x73f0"), Some("gfx1102"));
assert_eq!(pci_id_to_gfx("0x73bf"), Some("gfx1030"));
assert_eq!(pci_id_to_gfx("0x73a1"), Some("gfx1030"));
assert_eq!(pci_id_to_gfx("0x73df"), Some("gfx1031"));
assert_eq!(pci_id_to_gfx("0x73c3"), Some("gfx1031"));
assert_eq!(pci_id_to_gfx("0x73ff"), Some("gfx1032"));
assert_eq!(pci_id_to_gfx("0x73e3"), Some("gfx1032"));
assert_eq!(pci_id_to_gfx("0x743f"), Some("gfx1034"));
assert_eq!(pci_id_to_gfx("0x7421"), Some("gfx1034"));
assert_eq!(pci_id_to_gfx("0x9999"), None);
assert_eq!(pci_id_to_gfx("0x164e"), None); }
#[test]
fn pci_id_to_gfx_normalizes_case_and_prefix() {
assert_eq!(pci_id_to_gfx("0x744c"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("744C"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("744c"), Some("gfx1100"));
assert_eq!(pci_id_to_gfx("0X744C"), Some("gfx1100"));
}
#[test]
fn gfx_from_marketing_name_handles_navi_patterns() {
assert_eq!(gfx_from_marketing_name("Navi 48"), Some("gfx1201"));
assert_eq!(gfx_from_marketing_name("navi44"), Some("gfx1200"));
assert_eq!(gfx_from_marketing_name("Navi 31"), Some("gfx1100"));
assert_eq!(gfx_from_marketing_name("Navi 32"), Some("gfx1101"));
assert_eq!(gfx_from_marketing_name("Navi 33"), Some("gfx1102"));
assert_eq!(gfx_from_marketing_name("Navi 21"), Some("gfx1030"));
assert_eq!(gfx_from_marketing_name("Navi 22"), Some("gfx1031"));
assert_eq!(gfx_from_marketing_name("Navi 23"), Some("gfx1032"));
assert_eq!(gfx_from_marketing_name("Navi 24"), Some("gfx1034"));
assert_eq!(gfx_from_marketing_name("Generic GPU"), None);
}
#[test]
fn gfx_from_marketing_name_handles_rdnax_names() {
assert_eq!(
gfx_from_marketing_name("Radeon RX 7900 XTX"),
Some("gfx1100")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 7900 GRE"),
Some("gfx1100")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 7800 XT"),
Some("gfx1101")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 7700 XT"),
Some("gfx1101")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 7600 XT"),
Some("gfx1102")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 9070 XT"),
Some("gfx1201")
);
assert_eq!(gfx_from_marketing_name("Radeon RX 9060"), Some("gfx1200"));
assert_eq!(
gfx_from_marketing_name("Radeon RX 6900 XT"),
Some("gfx1030")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 6800 XT"),
Some("gfx1030")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 6700 XT"),
Some("gfx1031")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 6600 XT"),
Some("gfx1032")
);
assert_eq!(
gfx_from_marketing_name("Radeon RX 6500 XT"),
Some("gfx1034")
);
}
#[test]
fn class_filter_excludes_non_gpu_classes() {
let class_bridge = "0x060400";
assert_eq!(
u32::from_str_radix(class_bridge.strip_prefix("0x").unwrap(), 16).unwrap() & 0xFFFF00,
0x060400
);
let class_audio = "0x040300";
assert_eq!(
u32::from_str_radix(class_audio.strip_prefix("0x").unwrap(), 16).unwrap() & 0xFFFF00,
0x040300
);
let class_vga = "0x030000";
assert_eq!(
u32::from_str_radix(class_vga.strip_prefix("0x").unwrap(), 16).unwrap() & 0xFFFF00,
0x030000
);
let class_3d = "0x030200";
assert_eq!(
u32::from_str_radix(class_3d.strip_prefix("0x").unwrap(), 16).unwrap() & 0xFFFF00,
0x030200
);
}
}