Skip to main content

ironaccelerator_levelzero/
backend.rs

1//! Level Zero `Backend` impl. One entry per GPU or NPU device the loader
2//! returns; `ze_device_type_t` distinguishes the two.
3
4use ironaccelerator_core::{
5    Backend, BackendKind, Capability, CapabilityFlags, ComputeTier, DeviceDescriptor, DeviceId,
6    Result, Vendor,
7};
8
9use crate::drv::{EnumeratedDevice, ZE_DEVICE_TYPE_GPU, ZE_DEVICE_TYPE_VPU};
10
11pub struct LevelZeroBackend;
12pub static LEVELZERO_BACKEND: LevelZeroBackend = LevelZeroBackend;
13
14impl Backend for LevelZeroBackend {
15    fn kind(&self) -> BackendKind {
16        BackendKind::LevelZero
17    }
18
19    fn is_available(&self) -> bool {
20        crate::drv::is_available() && !crate::drv::enumerate().is_empty()
21    }
22
23    fn enumerate(&self) -> Result<Vec<DeviceDescriptor>> {
24        Ok(crate::drv::enumerate().into_iter().map(describe).collect())
25    }
26
27    fn capabilities(&self, device: u32) -> Result<CapabilityFlags> {
28        crate::drv::enumerate()
29            .into_iter()
30            .find(|d| d.ordinal == device)
31            .map(|d| flags_for(&d))
32            .ok_or(ironaccelerator_core::Error::InvalidArgument(
33                "level-zero device ordinal out of range",
34            ))
35    }
36}
37
38/// Level Zero GPUs expose BF16 from Xe-HPG/HPC on; NPU (VPU) advertises
39/// BF16 + INT8 from Meteor Lake onward. Expose the common superset.
40fn flags_for(d: &EnumeratedDevice) -> CapabilityFlags {
41    let mut flags = CapabilityFlags::FP32
42        | CapabilityFlags::FP16
43        | CapabilityFlags::BF16
44        | CapabilityFlags::INT8
45        | CapabilityFlags::MULTI_STREAM;
46    if d.type_ == ZE_DEVICE_TYPE_GPU {
47        flags |= CapabilityFlags::WMMA | CapabilityFlags::INT4;
48    }
49    flags
50}
51
52fn describe(d: EnumeratedDevice) -> DeviceDescriptor {
53    let vendor = match d.vendor_id {
54        0x8086 => Vendor::Intel,
55        0x10DE => Vendor::Nvidia,
56        0x1002 => Vendor::Amd,
57        _ => Vendor::Other,
58    };
59
60    let flags = flags_for(&d);
61    let (tier, arch_prefix) = match d.type_ {
62        ZE_DEVICE_TYPE_GPU => (ComputeTier::Consumer, "xe"),
63        ZE_DEVICE_TYPE_VPU => (ComputeTier::Mobile, "vpu"),
64        _ => (ComputeTier::Baseline, "ze"),
65    };
66
67    DeviceDescriptor {
68        id: DeviceId {
69            backend: BackendKind::LevelZero,
70            ordinal: d.ordinal,
71        },
72        vendor,
73        name: d.name,
74        arch: format!("{arch_prefix}-{:04x}", d.device_id),
75        total_memory_bytes: d.max_mem_alloc_size,
76        multiprocessor_count: d
77            .num_slices
78            .saturating_mul(d.num_subslices_per_slice)
79            .saturating_mul(d.num_eus_per_subslice),
80        clock_khz: d.core_clock_khz,
81        capability: Capability {
82            flags,
83            tier,
84            fp16_tflops: None,
85            fp8_tflops: None,
86            mem_bandwidth_gbs: None,
87        },
88    }
89}