use crate::DeviceLocation;
use crate::error::Result;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum BackendId {
Cpu,
Cuda,
Vulkan,
Metal,
}
impl BackendId {
pub fn as_str(self) -> &'static str {
match self {
BackendId::Cpu => "cpu",
BackendId::Cuda => "cuda",
BackendId::Vulkan => "vulkan",
BackendId::Metal => "metal",
}
}
}
impl std::fmt::Display for BackendId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DeviceDescriptor {
pub backend: BackendId,
pub device_index: u32,
pub hardware_sku: String,
pub vendor_id: u32,
pub device_id: u32,
pub compute_capability: Option<(u32, u32)>,
pub subgroup_width: Option<u32>,
pub driver_version: String,
pub total_memory_bytes: u64,
pub location: DeviceLocation,
}
impl DeviceDescriptor {
pub fn equivalence_key(&self) -> EquivalenceKey {
EquivalenceKey {
backend: self.backend,
vendor_id: self.vendor_id,
device_id: self.device_id,
compute_capability: self.compute_capability,
driver_version: self.driver_version.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct EquivalenceKey {
pub backend: BackendId,
pub vendor_id: u32,
pub device_id: u32,
pub compute_capability: Option<(u32, u32)>,
pub driver_version: String,
}
pub trait BackendProbe {
fn enumerate_devices() -> Result<Vec<DeviceDescriptor>>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn backend_id_as_str_is_stable() {
assert_eq!(BackendId::Cpu.as_str(), "cpu");
assert_eq!(BackendId::Cuda.as_str(), "cuda");
assert_eq!(BackendId::Vulkan.as_str(), "vulkan");
assert_eq!(BackendId::Metal.as_str(), "metal");
}
#[test]
fn equivalence_key_collapses_identical_cuda_devices() {
let make = |idx: u32| DeviceDescriptor {
backend: BackendId::Cuda,
device_index: idx,
hardware_sku: "NVIDIA GeForce RTX 4090".to_string(),
vendor_id: 0x10DE,
device_id: 0x2684,
compute_capability: Some((8, 9)),
subgroup_width: Some(32), driver_version: "550.54.14".to_string(),
total_memory_bytes: 25_769_803_776,
location: DeviceLocation::Cuda {
gpu_id: idx as usize,
},
};
let a = make(0);
let b = make(1);
assert_eq!(a.equivalence_key(), b.equivalence_key());
assert_ne!(a, b);
}
#[test]
fn equivalence_key_splits_same_silicon_across_backends() {
let cuda = DeviceDescriptor {
backend: BackendId::Cuda,
device_index: 0,
hardware_sku: "NVIDIA GeForce RTX 4090".to_string(),
vendor_id: 0x10DE,
device_id: 0x2684,
compute_capability: Some((8, 9)),
subgroup_width: Some(32),
driver_version: "550.54.14".to_string(),
total_memory_bytes: 25_769_803_776,
location: DeviceLocation::Cuda { gpu_id: 0 },
};
let vulkan = DeviceDescriptor {
backend: BackendId::Vulkan,
device_index: 0,
hardware_sku: "NVIDIA GeForce RTX 4090".to_string(),
vendor_id: 0x10DE,
device_id: 0x2684,
compute_capability: None, subgroup_width: Some(32),
driver_version: "550.54.14".to_string(),
total_memory_bytes: 25_769_803_776,
location: DeviceLocation::Cpu, };
assert_ne!(cuda.equivalence_key(), vulkan.equivalence_key());
}
#[test]
fn equivalence_key_splits_on_driver_version() {
let base = DeviceDescriptor {
backend: BackendId::Cuda,
device_index: 0,
hardware_sku: "X".to_string(),
vendor_id: 1,
device_id: 2,
compute_capability: Some((8, 9)),
subgroup_width: Some(32),
driver_version: "550".to_string(),
total_memory_bytes: 0,
location: DeviceLocation::Cuda { gpu_id: 0 },
};
let newer = DeviceDescriptor {
driver_version: "560".to_string(),
..base.clone()
};
assert_ne!(base.equivalence_key(), newer.equivalence_key());
}
#[test]
fn equivalence_key_ignores_subgroup_width() {
let base = DeviceDescriptor {
backend: BackendId::Vulkan,
device_index: 0,
hardware_sku: "X".to_string(),
vendor_id: 1,
device_id: 2,
compute_capability: None,
subgroup_width: None,
driver_version: "550".to_string(),
total_memory_bytes: 0,
location: DeviceLocation::Vulkan { gpu_id: 0 },
};
let with_width = DeviceDescriptor {
subgroup_width: Some(64),
..base.clone()
};
assert_ne!(base, with_width);
assert_eq!(base.equivalence_key(), with_width.equivalence_key());
}
}