use super::ids::{AdapterIdentity, AdapterLuid};
use windows::Win32::Graphics::Dxgi::{
CreateDXGIFactory1, DXGI_ADAPTER_DESC1, DXGI_ADAPTER_FLAG_SOFTWARE,
DXGI_MEMORY_SEGMENT_GROUP_LOCAL, DXGI_QUERY_VIDEO_MEMORY_INFO, IDXGIAdapter1, IDXGIAdapter3,
IDXGIFactory1,
};
use windows::core::Interface;
#[derive(Clone, Debug)]
pub struct DxgiAdapter {
pub identity: AdapterIdentity,
pub dedicated_video_memory: u64,
pub shared_system_memory: u64,
pub process_budget: Option<u64>,
pub process_current_usage: Option<u64>,
}
pub fn enumerate() -> Vec<DxgiAdapter> {
let Some(factory) = factory() else {
return Vec::new();
};
let mut adapters = Vec::new();
for index in 0.. {
let adapter: IDXGIAdapter1 = match unsafe { factory.EnumAdapters1(index) } {
Ok(adapter) => adapter,
Err(_) => break,
};
let desc: DXGI_ADAPTER_DESC1 = match unsafe { adapter.GetDesc1() } {
Ok(desc) => desc,
Err(_) => continue,
};
if desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32 != 0 {
continue;
}
let (process_budget, process_current_usage) = query_video_memory(&adapter);
adapters.push(DxgiAdapter {
identity: AdapterIdentity {
luid: AdapterLuid {
high: desc.AdapterLuid.HighPart,
low: desc.AdapterLuid.LowPart,
},
vendor_id: desc.VendorId,
device_id: desc.DeviceId,
description: widestring_to_string(&desc.Description),
},
dedicated_video_memory: desc.DedicatedVideoMemory as u64,
shared_system_memory: desc.SharedSystemMemory as u64,
process_budget,
process_current_usage,
});
}
adapters
}
static FACTORY: once_cell::sync::OnceCell<std::sync::Mutex<Option<SendFactory>>> =
once_cell::sync::OnceCell::new();
struct SendFactory(IDXGIFactory1);
unsafe impl Send for SendFactory {}
fn factory() -> Option<IDXGIFactory1> {
let cell = FACTORY.get_or_init(|| std::sync::Mutex::new(None));
let mut guard = match cell.lock() {
Ok(guard) => guard,
Err(poisoned) => poisoned.into_inner(),
};
if let Some(existing) = guard.as_ref() {
if unsafe { existing.0.IsCurrent() }.as_bool() {
return Some(existing.0.clone());
}
*guard = None;
}
let created: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?;
*guard = Some(SendFactory(created.clone()));
Some(created)
}
fn query_video_memory(adapter: &IDXGIAdapter1) -> (Option<u64>, Option<u64>) {
let Ok(adapter3) = adapter.cast::<IDXGIAdapter3>() else {
return (None, None);
};
let mut info = DXGI_QUERY_VIDEO_MEMORY_INFO::default();
if unsafe { adapter3.QueryVideoMemoryInfo(0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &mut info) }
.is_err()
{
return (None, None);
}
(Some(info.Budget), Some(info.CurrentUsage))
}
fn widestring_to_string(buffer: &[u16]) -> String {
let end = buffer.iter().position(|&c| c == 0).unwrap_or(buffer.len());
String::from_utf16_lossy(&buffer[..end]).trim().to_string()
}