#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub fn intel_gpu_marketing_name(device_id: u32) -> String {
let id = device_id & 0xFFFF;
match id {
0x5690..=0x5692 => "Intel Arc A770M / A730M / A550M".to_string(),
0x5693..=0x5695 => "Intel Arc A370M / A350M".to_string(),
0x56A0 | 0x56A1 => "Intel Arc A770".to_string(),
0x56A2 => "Intel Arc A750".to_string(),
0x56A3 | 0x56A4 => "Intel Arc A580".to_string(),
0x56A5 | 0x56A6 => "Intel Arc A380 / A310".to_string(),
0x56B0..=0x56B3 => "Intel Arc Pro A-series".to_string(),
0x56BA..=0x56BD => "Intel Arc A-series (mobile)".to_string(),
0xE202 | 0xE20B | 0xE20C | 0xE20D | 0xE210 | 0xE211 | 0xE212 | 0xE215 | 0xE216 => {
"Intel Arc B-series (Battlemage)".to_string()
}
0x7D40 | 0x7D41 | 0x7D45 | 0x7D55 | 0x7DD5 => {
"Intel Arc Graphics (Core Ultra / Meteor Lake)".to_string()
}
0x7D50 | 0x7D51 | 0x7D60 => "Intel Graphics (Core Ultra / Meteor Lake)".to_string(),
0x9A40 | 0x9A49 | 0x9A60 | 0x9A68 | 0x9A70 | 0x9A78 | 0x9AC0 | 0x9AC9 | 0x9AD9 | 0x9AF8 => {
"Intel Iris Xe Graphics (Tiger Lake)".to_string()
}
0x4680 | 0x4682 | 0x4688 | 0x468A | 0x468B | 0x4690 | 0x4692 | 0x4693 | 0x46A0 | 0x46A3
| 0x46A6 | 0x46A8 | 0x46AA | 0x46B0 | 0x46B3 | 0x46C0 | 0x46C3 | 0x46D0 | 0x46D1
| 0x46D2 | 0x46D3 | 0x46D4 => {
"Intel UHD / Iris Xe Graphics (Alder/Raptor Lake)".to_string()
}
0x4C8A | 0x4C8B | 0x4C8C | 0x4C90 | 0x4C9A => {
"Intel UHD Graphics (Rocket Lake)".to_string()
}
0x8A50 | 0x8A51 | 0x8A52 | 0x8A53 | 0x8A56 | 0x8A57 | 0x8A58 | 0x8A59 | 0x8A5A | 0x8A5B
| 0x8A5C | 0x8A5D | 0x8A71 => "Intel Iris Plus / UHD Graphics (Ice Lake)".to_string(),
0xB080..=0xB08F => "Intel Arc Graphics (Core Ultra / Panther Lake)".to_string(),
0xA780 | 0xA781 | 0xA782 | 0xA783 | 0xA788 | 0xA789 | 0xA78A | 0xA78B | 0xA7A0 | 0xA7A1
| 0xA7A8 | 0xA7A9 | 0xA7AA | 0xA7AB | 0xA7AC | 0xA7AD => {
"Intel Graphics (Arrow/Lunar Lake)".to_string()
}
_ => String::new(),
}
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub fn resolve_intel_gpu_name(device_id: u32) -> String {
let curated = intel_gpu_marketing_name(device_id);
if curated.is_empty() {
format!("Intel Graphics (device {:#06x})", device_id & 0xFFFF)
} else {
curated
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IntelArchitecture {
Alchemist,
Battlemage,
XeLpg,
XeLpgPlus,
Xe3,
IrisXe,
OlderIntegrated,
Unknown,
}
impl IntelArchitecture {
pub fn is_sycl_capable(self) -> bool {
matches!(
self,
Self::Alchemist
| Self::Battlemage
| Self::XeLpg
| Self::XeLpgPlus
| Self::Xe3
| Self::IrisXe,
)
}
pub fn label(self) -> &'static str {
match self {
Self::Alchemist => "Alchemist (Xe-HPG, A-series)",
Self::Battlemage => "Battlemage (Xe2, B-series)",
Self::XeLpg => "Xe-LPG (Meteor Lake)",
Self::XeLpgPlus => "Xe-LPG+ (Lunar Lake)",
Self::Xe3 => "Xe3 (Panther Lake)",
Self::IrisXe => "Iris Xe (Tiger/Alder/Raptor Lake)",
Self::OlderIntegrated => "Pre-Xe (HD/UHD Graphics)",
Self::Unknown => "Unknown",
}
}
pub fn sycl_capable_label(self) -> &'static str {
match self {
Self::Unknown => "Unknown",
_ if self.is_sycl_capable() => "Yes",
_ => "No",
}
}
}
pub fn classify_intel_architecture(name: &str) -> IntelArchitecture {
let n = name.to_lowercase();
if (n.contains("hd graphics") || n.contains("uhd graphics"))
&& !n.contains("iris")
&& !n.contains("arc")
&& !n.contains("xe")
{
return IntelArchitecture::OlderIntegrated;
}
if n.contains("battlemage")
|| (n.contains("arc") && (n.contains("b580") || n.contains("b570") || n.contains("b380")))
{
return IntelArchitecture::Battlemage;
}
if n.contains("arc") && (n.contains("a3") || n.contains("a5") || n.contains("a7")) {
return IntelArchitecture::Alchemist;
}
if n.contains("lunarlake")
|| n.contains("lunar lake")
|| (n.contains("arc") && (n.contains("140v") || n.contains("130v")))
{
return IntelArchitecture::XeLpgPlus;
}
if n.contains("panther lake")
|| n.contains("pantherlake")
|| n.contains("xe3")
|| (n.contains("arc") && XE3_INTEGRATED_MODELS.iter().any(|m| n.contains(m)))
{
return IntelArchitecture::Xe3;
}
if n.contains("xe") && n.contains("lpg") {
return IntelArchitecture::XeLpg;
}
if n.contains("arc") && n.contains("graphics") && !contains_arc_model_number(&n) {
return IntelArchitecture::XeLpg;
}
if n.contains("iris") && n.contains("xe") {
return IntelArchitecture::IrisXe;
}
IntelArchitecture::Unknown
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
const DISCRETE_ARC_MODELS: &[&str] = &[
"a310", "a380", "a580", "a750", "a770", "b380", "b570", "b580",
];
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
const INTEGRATED_ARC_MODELS: &[&str] = &["130v", "140v", "b390"];
const XE3_INTEGRATED_MODELS: &[&str] = &["b390"];
fn contains_arc_model_number(lower: &str) -> bool {
lower
.split(|c: char| !c.is_ascii_alphanumeric())
.any(is_arc_model_token)
}
fn is_arc_model_token(token: &str) -> bool {
let bytes = token.as_bytes();
if bytes.len() < 4 {
return false;
}
let letter_first =
matches!(bytes[0], b'a' | b'b' | b'c' | b'd') && bytes[1..].iter().all(u8::is_ascii_digit);
let suffix_last =
bytes[bytes.len() - 1] == b'v' && bytes[..bytes.len() - 1].iter().all(u8::is_ascii_digit);
letter_first || suffix_last
}
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
pub fn classify_intel_variant(name: &str) -> Option<&'static str> {
let lower = name.to_lowercase();
if !lower.contains("arc") {
return Some("Integrated");
}
if DISCRETE_ARC_MODELS.iter().any(|m| lower.contains(m)) {
return Some("Discrete");
}
if INTEGRATED_ARC_MODELS.iter().any(|m| lower.contains(m)) {
return Some("Integrated");
}
if contains_arc_model_number(&lower) {
return None;
}
Some("Integrated")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn b390_is_integrated_not_discrete() {
assert_eq!(
classify_intel_variant("Intel(R) Arc(TM) B390 Graphics"),
Some("Integrated")
);
}
#[test]
fn b390_classifies_as_xe3_not_meteor_lake() {
let arch = classify_intel_architecture("Intel(R) Arc(TM) B390 Graphics");
assert_eq!(arch, IntelArchitecture::Xe3);
assert_eq!(arch.label(), "Xe3 (Panther Lake)");
assert!(arch.is_sycl_capable());
}
#[test]
fn b380_and_b390_do_not_collide() {
assert_eq!(
classify_intel_variant("Intel(R) Arc(TM) B380 Graphics"),
Some("Discrete")
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) B380 Graphics"),
IntelArchitecture::Battlemage
);
assert_eq!(
classify_intel_variant("Intel(R) Arc(TM) B390 Graphics"),
Some("Integrated")
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) B390 Graphics"),
IntelArchitecture::Xe3
);
}
#[test]
fn unknown_numbered_arc_defers_instead_of_guessing() {
assert_eq!(
classify_intel_variant("Intel(R) Arc(TM) C990 Graphics"),
None
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) C990 Graphics"),
IntelArchitecture::Unknown
);
}
#[test]
fn known_discrete_arc_skus_stay_discrete() {
for name in [
"Intel(R) Arc(TM) A770 Graphics",
"Intel(R) Arc(TM) A750 Graphics",
"Intel(R) Arc(TM) A580 Graphics",
"Intel(R) Arc(TM) A380 Graphics",
"Intel(R) Arc(TM) A310 Graphics",
"Intel(R) Arc(TM) B580 Graphics",
"Intel(R) Arc(TM) B570 Graphics",
] {
assert_eq!(
classify_intel_variant(name),
Some("Discrete"),
"{name} must stay discrete"
);
}
}
#[test]
fn integrated_families_stay_integrated() {
for name in [
"Intel(R) Iris(R) Xe Graphics",
"Intel(R) UHD Graphics 770",
"Intel(R) HD Graphics 620",
"Intel(R) Arc(TM) Graphics",
"Intel(R) Arc(TM) 140V GPU",
"Intel(R) Arc(TM) 130V GPU",
] {
assert_eq!(
classify_intel_variant(name),
Some("Integrated"),
"{name} must stay integrated"
);
}
}
#[test]
fn neighbouring_architectures_do_not_regress() {
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) Graphics"),
IntelArchitecture::XeLpg
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) 140V GPU"),
IntelArchitecture::XeLpgPlus
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) A770 Graphics"),
IntelArchitecture::Alchemist
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) B580 Graphics"),
IntelArchitecture::Battlemage
);
}
#[test]
fn arc_model_number_detection() {
assert!(contains_arc_model_number("intel(r) arc(tm) a770 graphics"));
assert!(contains_arc_model_number("intel(r) arc(tm) b390 graphics"));
assert!(contains_arc_model_number("intel(r) arc(tm) 140v gpu"));
assert!(!contains_arc_model_number("intel(r) arc(tm) graphics"));
assert!(!contains_arc_model_number("intel(r) iris(r) xe graphics"));
assert!(!contains_arc_model_number("a77"));
}
#[test]
fn known_families_resolve() {
assert!(intel_gpu_marketing_name(0x56A0).contains("Arc A770"));
assert!(intel_gpu_marketing_name(0x56A2).contains("Arc A750"));
assert!(intel_gpu_marketing_name(0xE20B).contains("Battlemage"));
assert!(intel_gpu_marketing_name(0x7D40).contains("Meteor Lake"));
assert!(intel_gpu_marketing_name(0x9A49).contains("Tiger Lake"));
assert!(intel_gpu_marketing_name(0x46A6).contains("Alder/Raptor Lake"));
assert!(intel_gpu_marketing_name(0x4C8A).contains("Rocket Lake"));
assert!(intel_gpu_marketing_name(0x8A50).contains("Ice Lake"));
assert!(intel_gpu_marketing_name(0xA780).contains("Arrow/Lunar Lake"));
}
#[test]
fn unknown_falls_back_to_generic() {
let n = resolve_intel_gpu_name(0x1234);
assert!(n.starts_with("Intel Graphics (device"));
assert!(n.contains("0x1234"));
}
#[test]
fn high_bits_ignored() {
assert!(resolve_intel_gpu_name(0x0000_56A0).contains("Arc A770"));
assert!(resolve_intel_gpu_name(0xFFFF_56A0).contains("Arc A770"));
}
#[test]
fn classifies_arc_a_series_as_alchemist() {
for name in &[
"Intel Arc A770 Graphics",
"Intel Arc A750",
"Intel Arc A580",
"Intel Arc A380",
"Intel Arc A310",
"Intel(R) Arc(TM) A770 Graphics",
] {
assert_eq!(
classify_intel_architecture(name),
IntelArchitecture::Alchemist,
"mis-classified: {name}"
);
assert!(IntelArchitecture::Alchemist.is_sycl_capable());
}
}
#[test]
fn classifies_battlemage_b_series() {
for name in &[
"Intel Battlemage Graphics",
"Intel(R) Battlemage(TM) Graphics",
"Intel Arc B580",
"Intel(R) Arc(TM) B580 Graphics",
] {
assert_eq!(
classify_intel_architecture(name),
IntelArchitecture::Battlemage,
"mis-classified: {name}"
);
assert!(IntelArchitecture::Battlemage.is_sycl_capable());
}
}
#[test]
fn classifies_core_ultra_integrated_arc_as_xe_lpg() {
assert_eq!(
classify_intel_architecture("Intel Arc Graphics"),
IntelArchitecture::XeLpg,
);
assert_eq!(
classify_intel_architecture("Intel(R) Arc(TM) Graphics"),
IntelArchitecture::XeLpg,
);
assert!(IntelArchitecture::XeLpg.is_sycl_capable());
}
#[test]
fn classifies_lunar_lake_arc_140v() {
let result = classify_intel_architecture("Intel Arc 140V Graphics");
assert!(
matches!(
result,
IntelArchitecture::XeLpgPlus | IntelArchitecture::XeLpg
),
"Arc 140V should classify as a Lunar Lake / Xe-LPG-family part, got {result:?}",
);
assert!(result.is_sycl_capable());
let result_130v = classify_intel_architecture("Intel Arc 130V Graphics");
assert!(
matches!(
result_130v,
IntelArchitecture::XeLpgPlus | IntelArchitecture::XeLpg
),
"Arc 130V should classify as a Lunar Lake / Xe-LPG-family part, got {result_130v:?}",
);
}
#[test]
fn classifies_iris_xe_as_iris_xe() {
for name in &["Intel Iris Xe Graphics", "Intel(R) Iris(R) Xe Graphics"] {
assert_eq!(
classify_intel_architecture(name),
IntelArchitecture::IrisXe,
"mis-classified: {name}"
);
assert!(IntelArchitecture::IrisXe.is_sycl_capable());
}
}
#[test]
fn classifies_xe_lpg_meteor_lake() {
assert_eq!(
classify_intel_architecture("Intel Xe-LPG Graphics"),
IntelArchitecture::XeLpg,
);
}
#[test]
fn classifies_lunar_lake_explicit() {
for name in &[
"Intel LunarLake Graphics",
"Intel(R) LunarLake(TM) Graphics",
"Intel Lunar Lake Graphics",
] {
assert_eq!(
classify_intel_architecture(name),
IntelArchitecture::XeLpgPlus,
"mis-classified: {name}"
);
assert!(IntelArchitecture::XeLpgPlus.is_sycl_capable());
}
}
#[test]
fn older_integrated_is_not_sycl_capable() {
for name in &[
"Intel HD Graphics 630",
"Intel UHD Graphics 770",
"Intel HD Graphics 520",
"Intel UHD Graphics 620",
] {
let arch = classify_intel_architecture(name);
assert_eq!(
arch,
IntelArchitecture::OlderIntegrated,
"mis-classified: {name}"
);
assert!(!arch.is_sycl_capable(), "{name} should not be SYCL capable");
}
}
#[test]
fn unknown_names_classified_as_unknown() {
let arch = classify_intel_architecture("Definitely Not An Intel GPU");
assert_eq!(arch, IntelArchitecture::Unknown);
assert!(!arch.is_sycl_capable());
assert_eq!(classify_intel_architecture(""), IntelArchitecture::Unknown);
}
#[test]
fn architecture_labels_are_stable() {
assert_eq!(
IntelArchitecture::Alchemist.label(),
"Alchemist (Xe-HPG, A-series)"
);
assert_eq!(
IntelArchitecture::Battlemage.label(),
"Battlemage (Xe2, B-series)"
);
assert_eq!(IntelArchitecture::XeLpg.label(), "Xe-LPG (Meteor Lake)");
assert_eq!(IntelArchitecture::XeLpgPlus.label(), "Xe-LPG+ (Lunar Lake)");
assert_eq!(
IntelArchitecture::IrisXe.label(),
"Iris Xe (Tiger/Alder/Raptor Lake)"
);
assert_eq!(
IntelArchitecture::OlderIntegrated.label(),
"Pre-Xe (HD/UHD Graphics)"
);
assert_eq!(IntelArchitecture::Unknown.label(), "Unknown");
}
#[test]
fn sycl_capability_matches_backend_ai_go() {
assert!(IntelArchitecture::Alchemist.is_sycl_capable());
assert!(IntelArchitecture::Battlemage.is_sycl_capable());
assert!(IntelArchitecture::XeLpg.is_sycl_capable());
assert!(IntelArchitecture::XeLpgPlus.is_sycl_capable());
assert!(IntelArchitecture::IrisXe.is_sycl_capable());
assert!(!IntelArchitecture::OlderIntegrated.is_sycl_capable());
assert!(!IntelArchitecture::Unknown.is_sycl_capable());
}
#[test]
fn sycl_capable_label_distinguishes_unknown_from_no() {
assert_eq!(IntelArchitecture::Alchemist.sycl_capable_label(), "Yes");
assert_eq!(IntelArchitecture::Battlemage.sycl_capable_label(), "Yes");
assert_eq!(IntelArchitecture::XeLpg.sycl_capable_label(), "Yes");
assert_eq!(IntelArchitecture::XeLpgPlus.sycl_capable_label(), "Yes");
assert_eq!(IntelArchitecture::IrisXe.sycl_capable_label(), "Yes");
assert_eq!(
IntelArchitecture::OlderIntegrated.sycl_capable_label(),
"No"
);
assert_eq!(IntelArchitecture::Unknown.sycl_capable_label(), "Unknown");
}
#[test]
fn panther_lake_device_ids_resolve_to_a_named_part() {
for id in [0xB080u32, 0xB084, 0xB08F] {
assert_eq!(
intel_gpu_marketing_name(id),
"Intel Arc Graphics (Core Ultra / Panther Lake)",
"device {id:#06x}"
);
}
}
#[test]
fn the_vendor_half_of_a_pci_id_is_ignored() {
assert_eq!(
resolve_intel_gpu_name(0x8086_B080),
"Intel Arc Graphics (Core Ultra / Panther Lake)"
);
}
#[test]
fn the_panther_lake_range_does_not_bleed() {
assert!(intel_gpu_marketing_name(0xB07F).is_empty());
assert!(intel_gpu_marketing_name(0xB090).is_empty());
assert_eq!(
resolve_intel_gpu_name(0xB090),
"Intel Graphics (device 0xb090)"
);
}
#[test]
fn a_resolved_panther_lake_name_classifies_as_xe3() {
let name = resolve_intel_gpu_name(0xB080);
assert_eq!(classify_intel_architecture(&name), IntelArchitecture::Xe3);
assert_eq!(classify_intel_variant(&name), Some("Integrated"));
}
}