use crate::device::GpuReader;
use crate::device::readers::intel_gpu_names::classify_intel_architecture;
use crate::device::types::{GpuInfo, ProcessInfo};
use crate::utils::get_hostname;
use chrono::Local;
use serde::Deserialize;
use std::collections::HashMap;
#[cfg(feature = "level_zero")]
use std::sync::Mutex;
use wmi::WMIConnection;
thread_local! {
static WMI_CONNECTION: std::cell::RefCell<Option<WMIConnection>> =
const { std::cell::RefCell::new(None) };
}
fn with_wmi_connection<T, F: FnOnce(&WMIConnection) -> T>(f: F) -> Option<T> {
WMI_CONNECTION.with(|cell| {
let mut conn_ref = cell.borrow_mut();
if conn_ref.is_none() {
match WMIConnection::new() {
Ok(wmi_con) => {
*conn_ref = Some(wmi_con);
}
Err(e) => {
eprintln!("Intel GPU: Failed to create WMI connection: {e}");
}
}
}
conn_ref.as_ref().map(f)
})
}
#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "PascalCase")]
struct Win32VideoController {
name: Option<String>,
adapter_r_a_m: Option<u64>,
driver_version: Option<String>,
video_processor: Option<String>,
pnp_device_i_d: Option<String>,
status: Option<String>,
adapter_d_a_c_type: Option<String>,
}
#[derive(Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
struct VideoControllerName {
name: Option<String>,
}
pub struct IntelWindowsGpuReader {
#[cfg(feature = "level_zero")]
level_zero_state:
Mutex<HashMap<String, crate::device::readers::intel_gpu_level_zero::LevelZeroState>>,
}
impl Default for IntelWindowsGpuReader {
fn default() -> Self {
Self::new()
}
}
impl IntelWindowsGpuReader {
pub fn new() -> Self {
Self {
#[cfg(feature = "level_zero")]
level_zero_state: Mutex::new(HashMap::new()),
}
}
fn query_intel_gpus(&self) -> Vec<GpuInfo> {
with_wmi_connection(|wmi_con| {
let mut gpu_list = Vec::new();
let result: Result<Vec<Win32VideoController>, _> = wmi_con.raw_query(
"SELECT Name, AdapterRAM, DriverVersion, VideoProcessor, PNPDeviceID, Status, AdapterDACType FROM Win32_VideoController",
);
if let Ok(controllers) = result {
let hostname = get_hostname();
let time = Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
for (idx, controller) in controllers.iter().enumerate() {
let name = controller.name.clone().unwrap_or_default();
if !is_intel_gpu_name(&name) {
continue;
}
let uuid = controller
.pnp_device_i_d
.clone()
.unwrap_or_else(|| format!("Intel-GPU-{idx}"));
let total_memory = controller.adapter_r_a_m.unwrap_or(0);
const FOUR_GB: u64 = 4 * 1024 * 1024 * 1024;
if total_memory == 0 {
eprintln!("Intel GPU '{name}': VRAM size unavailable (reported as 0)");
} else if total_memory >= FOUR_GB - (512 * 1024 * 1024) {
eprintln!(
"Intel GPU '{name}': VRAM reported as {total_memory} bytes, may be inaccurate for >4GB GPUs due to WMI 32-bit limitation"
);
}
let mut detail = HashMap::new();
if let Some(ref driver) = controller.driver_version {
detail.insert("Driver Version".to_string(), driver.clone());
}
if let Some(ref processor) = controller.video_processor {
detail.insert("Video Processor".to_string(), processor.clone());
}
if let Some(ref status) = controller.status {
detail.insert("Status".to_string(), status.clone());
}
if let Some(ref dac_type) = controller.adapter_d_a_c_type {
detail.insert("DAC Type".to_string(), dac_type.clone());
}
detail.insert(
"Variant".to_string(),
classify_intel_variant(&name).to_string(),
);
let arch = classify_intel_architecture(&name);
detail.insert("Architecture".to_string(), arch.label().to_string());
detail.insert(
"SYCL Capable".to_string(),
arch.sycl_capable_label().to_string(),
);
detail.insert(
"Metrics Source".to_string(),
"WMI".to_string(),
);
detail.insert(
"Note".to_string(),
"Detailed metrics require Level Zero / xpu-smi".to_string(),
);
detail.insert("Source: Utilization".to_string(), "unavailable".to_string());
detail.insert("Source: Temperature".to_string(), "unavailable".to_string());
detail.insert("Source: Power".to_string(), "unavailable".to_string());
detail.insert("Source: Frequency".to_string(), "unavailable".to_string());
detail.insert(
"Source: Memory".to_string(),
if total_memory > 0 { "WMI" } else { "unavailable" }.to_string(),
);
detail.insert("Source: Fan".to_string(), "unavailable".to_string());
gpu_list.push(GpuInfo {
uuid,
time: time.clone(),
name,
device_type: "GPU".to_string(),
host_id: hostname.clone(),
hostname: hostname.clone(),
instance: hostname.clone(),
utilization: 0.0,
ane_utilization: 0.0,
dla_utilization: None,
tensorcore_utilization: None,
temperature: 0,
used_memory: 0,
total_memory,
frequency: 0,
power_consumption: 0.0,
gpu_core_count: None,
temperature_threshold_slowdown: None,
temperature_threshold_shutdown: None,
temperature_threshold_max_operating: None,
temperature_threshold_acoustic: None,
performance_state: None,
numa_node_id: None,
gsp_firmware_mode: None,
gsp_firmware_version: None,
nvlink_remote_devices: Vec::new(),
gpm_metrics: None,
detail,
});
}
}
gpu_list
})
.unwrap_or_default()
}
}
impl GpuReader for IntelWindowsGpuReader {
fn get_gpu_info(&self) -> Vec<GpuInfo> {
let mut gpus = self.query_intel_gpus();
#[cfg(feature = "level_zero")]
self.augment_with_level_zero(&mut gpus);
gpus
}
fn get_process_info(&self) -> Vec<ProcessInfo> {
Vec::new()
}
}
#[cfg(feature = "level_zero")]
impl IntelWindowsGpuReader {
fn augment_with_level_zero(&self, gpus: &mut [GpuInfo]) {
use crate::device::readers::intel_gpu_level_zero as l0;
let bdfs = l0::enumerated_pci_bdfs();
if bdfs.is_empty() {
return;
}
let mut states = match self.level_zero_state.lock() {
Ok(s) => s,
Err(_) => return,
};
for (gpu, bdf) in gpus.iter_mut().zip(bdfs.iter()) {
let state = states
.entry(gpu.uuid.clone())
.or_insert_with(l0::LevelZeroState::empty);
if let Some(readout) = l0::refresh(state, bdf) {
l0::apply_to_gpu_info(gpu, &readout, l0::ApplyPlatform::Windows);
}
}
}
}
pub fn has_intel_gpu_windows() -> bool {
let wmi_con = match WMIConnection::new() {
Ok(w) => w,
Err(e) => {
eprintln!("Intel GPU detection: Failed to create WMI connection: {e}");
return false;
}
};
let query_result: Result<Vec<VideoControllerName>, _> =
wmi_con.raw_query("SELECT Name FROM Win32_VideoController");
match query_result {
Ok(controllers) => {
for controller in controllers {
if let Some(name) = &controller.name
&& is_intel_gpu_name(name)
{
return true;
}
}
}
Err(e) => {
eprintln!("Intel GPU detection: WMI query failed: {e}");
return false;
}
}
false
}
pub fn is_intel_gpu_name(name: &str) -> bool {
let lower = name.to_lowercase();
if !lower.contains("intel") {
return false;
}
const FAMILY_TOKENS: &[&str] = &[
"arc",
"iris",
"uhd graphics",
"hd graphics",
"xe graphics",
"intel graphics",
"xe-lpg",
"battlemage",
"lunarlake",
"lunar lake",
];
FAMILY_TOKENS.iter().any(|t| lower.contains(t))
}
fn classify_intel_variant(name: &str) -> &'static str {
let lower = name.to_lowercase();
if !lower.contains("arc") {
return "Integrated";
}
let has_model_number = lower
.split(|c: char| !c.is_ascii_alphanumeric())
.any(is_arc_model_token);
if has_model_number {
"Discrete"
} else {
"Integrated"
}
}
fn is_arc_model_token(token: &str) -> bool {
let bytes = token.as_bytes();
if bytes.len() < 4 {
return false;
}
let first = bytes[0] as char;
if !matches!(first, 'a' | 'b' | 'c' | 'd') {
return false;
}
bytes[1..].iter().all(|b| b.is_ascii_digit())
}
#[cfg(test)]
#[path = "intel_gpu_windows/tests.rs"]
mod tests;