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()
}
#[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"));
}
}