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