use windows::Win32::Graphics::Dxgi::{
CreateDXGIFactory1, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, DXGI_QUERY_VIDEO_MEMORY_INFO,
IDXGIAdapter, IDXGIAdapter3, IDXGIFactory1,
};
use windows::core::Interface;
const NVIDIA_VENDOR_ID: u32 = 0x10DE;
const MICROSOFT_BASIC_VENDOR_ID: u32 = 0x1414;
pub(super) struct DxgiQueryResult {
pub current_usage: u64,
pub dedicated_video_memory: u64,
pub adapter_name: Option<String>,
}
#[allow(unsafe_code)]
pub(super) fn query(idx: u32) -> Option<DxgiQueryResult> {
let factory: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?;
let mut raw_idx: u32 = 0;
let mut nvidia_count: u32 = 0;
loop {
let adapter1 = unsafe { factory.EnumAdapters1(raw_idx) }.ok()?;
let adapter: IDXGIAdapter = adapter1.cast().ok()?;
let desc = unsafe { adapter.GetDesc() }.ok()?;
if desc.VendorId == NVIDIA_VENDOR_ID && desc.DedicatedVideoMemory > 0 {
if nvidia_count == idx {
#[allow(clippy::as_conversions)]
let total = desc.DedicatedVideoMemory as u64;
let raw_name = String::from_utf16_lossy(&desc.Description);
let trimmed = raw_name.trim_end_matches('\0');
let adapter_name = if trimmed.is_empty() {
None
} else {
Some(trimmed.to_owned())
};
let adapter3: IDXGIAdapter3 = adapter.cast().ok()?;
let mut mem_info = DXGI_QUERY_VIDEO_MEMORY_INFO::default();
unsafe {
adapter3.QueryVideoMemoryInfo(
0,
DXGI_MEMORY_SEGMENT_GROUP_LOCAL,
&raw mut mem_info,
)
}
.ok()?;
#[cfg(feature = "debug-output")]
eprintln!(
"[DXGI debug] adapter[nvidia#{idx} raw#{raw_idx}]: name={adapter_name:?}, \
dedicated_vram={total}, current_usage={}, budget={}",
mem_info.CurrentUsage, mem_info.Budget
);
return Some(DxgiQueryResult {
current_usage: mem_info.CurrentUsage,
dedicated_video_memory: total,
adapter_name,
});
}
nvidia_count += 1;
}
raw_idx += 1;
}
}
#[allow(unsafe_code)]
pub(super) fn adapter_name(idx: u32) -> Option<String> {
let factory: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?;
let mut raw_idx: u32 = 0;
let mut nvidia_count: u32 = 0;
loop {
let adapter1 = unsafe { factory.EnumAdapters1(raw_idx) }.ok()?;
let adapter: IDXGIAdapter = adapter1.cast().ok()?;
let desc = unsafe { adapter.GetDesc() }.ok()?;
if desc.VendorId == NVIDIA_VENDOR_ID && desc.DedicatedVideoMemory > 0 {
if nvidia_count == idx {
let raw_name = String::from_utf16_lossy(&desc.Description);
let trimmed = raw_name.trim_end_matches('\0');
return if trimmed.is_empty() {
None
} else {
Some(trimmed.to_owned())
};
}
nvidia_count += 1;
}
raw_idx += 1;
}
}
pub(super) struct DxgiAdapterEntry {
pub adapter_name: Option<String>,
pub current_usage: u64,
pub dedicated_video_memory: u64,
pub shared_system_memory: u64,
}
#[allow(unsafe_code)]
#[must_use]
pub(super) fn enumerate_non_nvidia() -> Vec<DxgiAdapterEntry> {
let Ok(factory) = (unsafe { CreateDXGIFactory1::<IDXGIFactory1>() }) else {
return Vec::new();
};
let mut out: Vec<DxgiAdapterEntry> = Vec::new();
let mut raw_idx: u32 = 0;
loop {
let Ok(adapter1) = (unsafe { factory.EnumAdapters1(raw_idx) }) else {
break;
};
let Ok(adapter) = adapter1.cast::<IDXGIAdapter>() else {
break;
};
let Ok(desc) = (unsafe { adapter.GetDesc() }) else {
break;
};
#[allow(clippy::as_conversions)]
let dedicated = desc.DedicatedVideoMemory as u64;
#[allow(clippy::as_conversions)]
let shared = desc.SharedSystemMemory as u64;
let qualifies = desc.VendorId != NVIDIA_VENDOR_ID
&& desc.VendorId != MICROSOFT_BASIC_VENDOR_ID
&& (dedicated > 0 || shared > 0);
if qualifies {
let raw_name = String::from_utf16_lossy(&desc.Description);
let trimmed = raw_name.trim_end_matches('\0');
let adapter_name = if trimmed.is_empty() {
None
} else {
Some(trimmed.to_owned())
};
let current_usage = adapter
.cast::<IDXGIAdapter3>()
.ok()
.and_then(|a3| {
let mut info = DXGI_QUERY_VIDEO_MEMORY_INFO::default();
unsafe {
a3.QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &raw mut info)
}
.ok()
.map(|()| info.CurrentUsage)
})
.unwrap_or(0);
#[cfg(feature = "debug-output")]
eprintln!(
"[DXGI debug] non-nvidia adapter[raw#{raw_idx}]: vendor={:#x}, \
name={adapter_name:?}, dedicated={dedicated}, shared={shared}, \
current_usage={current_usage}",
desc.VendorId
);
out.push(DxgiAdapterEntry {
adapter_name,
current_usage,
dedicated_video_memory: dedicated,
shared_system_memory: shared,
});
}
raw_idx += 1;
}
out
}
#[allow(unsafe_code)]
pub(super) fn device_count() -> Option<u32> {
let factory: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?;
let mut raw_idx: u32 = 0;
let mut count: u32 = 0;
loop {
let Ok(adapter1) = (unsafe { factory.EnumAdapters1(raw_idx) }) else {
break;
};
let Ok(adapter) = adapter1.cast::<IDXGIAdapter>() else {
break;
};
let Ok(desc) = (unsafe { adapter.GetDesc() }) else {
break;
};
if desc.VendorId == NVIDIA_VENDOR_ID && desc.DedicatedVideoMemory > 0 {
count += 1;
}
raw_idx += 1;
}
#[cfg(feature = "debug-output")]
eprintln!("[DXGI debug] device_count = {count} (NVIDIA adapters with non-zero VRAM)");
Some(count)
}