Skip to main content

ironaccelerator_levelzero/
drv.rs

1//! Minimal `ze_loader` driver: `zeInit`, driver walk, device walk,
2//! `zeDeviceGetProperties`. Enough for the planner; kernel launch lives
3//! in higher layers.
4//!
5//! `drop(sym)` on a `libloading::Symbol` is intentional — it releases the
6//! borrow on the Library so the next `lib.get(...)` can proceed. Symbol is
7//! `Copy` and doesn't impl `Drop`, but the borrow lives in its lifetime
8//! parameter, which `drop` does end. We silence the spurious lint module-wide.
9//!
10//! Everything is loaded via `libloading` so the crate compiles on hosts
11//! without Level Zero installed; the backend simply reports unavailable.
12
13#![allow(clippy::drop_non_drop)] // see module docs above
14
15use core::ffi::c_void;
16use libloading::{Library, Symbol};
17use once_cell::sync::OnceCell;
18
19// ── Raw FFI types ──────────────────────────────────────────────────────────
20
21/// Mirrors `ze_result_t`; `0` is success.
22pub type ZeResult = u32;
23/// `ZE_RESULT_SUCCESS` value of `ze_result_t`.
24pub const ZE_RESULT_SUCCESS: ZeResult = 0;
25
26/// `ze_driver_handle_t`.
27pub type ZeDriverHandle = *mut c_void;
28/// `ze_device_handle_t`.
29pub type ZeDeviceHandle = *mut c_void;
30
31/// `ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES` value of `ze_structure_type_t`, for `ze_device_properties_t::stype`.
32pub const ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES: u32 = 0x1;
33
34/// `ZE_DEVICE_TYPE_GPU` value of `ze_device_type_t`: integrated or discrete GPU.
35pub const ZE_DEVICE_TYPE_GPU: u32 = 1;
36/// `ZE_DEVICE_TYPE_CPU` value of `ze_device_type_t`: CPU device.
37pub const ZE_DEVICE_TYPE_CPU: u32 = 2;
38/// `ZE_DEVICE_TYPE_FPGA` value of `ze_device_type_t`: FPGA device.
39pub const ZE_DEVICE_TYPE_FPGA: u32 = 3;
40/// `ZE_DEVICE_TYPE_MCA` value of `ze_device_type_t`: memory copy accelerator.
41pub const ZE_DEVICE_TYPE_MCA: u32 = 4;
42/// `ZE_DEVICE_TYPE_VPU` value of `ze_device_type_t`: vision processing unit (NPU).
43pub const ZE_DEVICE_TYPE_VPU: u32 = 5;
44
45/// Length of `ze_device_properties_t::name` in bytes.
46pub const ZE_MAX_DEVICE_NAME: usize = 256;
47
48/// Mirrors `ze_device_uuid_t`.
49#[repr(C)]
50#[derive(Clone, Copy)]
51pub struct ZeDeviceUuid {
52    /// Raw 16-byte UUID.
53    pub id: [u8; 16],
54}
55
56/// Mirrors `ze_device_properties_t`.
57#[repr(C)]
58#[derive(Clone, Copy)]
59pub struct ZeDeviceProperties {
60    /// `ze_device_properties_t::stype`.
61    pub stype: u32,
62    /// `ze_device_properties_t::pNext`.
63    pub p_next: *mut c_void,
64    /// `ze_device_properties_t::type`, one of the `ZE_DEVICE_TYPE_*` values.
65    pub type_: u32,
66    /// `ze_device_properties_t::vendorId`: PCI vendor ID.
67    pub vendor_id: u32,
68    /// `ze_device_properties_t::deviceId`: PCI device ID.
69    pub device_id: u32,
70    /// `ze_device_properties_t::subdeviceId`.
71    pub subdevice_id: u32,
72    /// `ze_device_properties_t::coreClockRate`, in MHz.
73    pub core_clock_rate: u32,
74    /// `ze_device_properties_t::maxMemAllocSize`, in bytes.
75    pub max_mem_alloc_size: u64,
76    /// `ze_device_properties_t::maxHardwareContexts`.
77    pub max_hardware_contexts: u32,
78    /// `ze_device_properties_t::maxCommandQueuePriority`.
79    pub max_command_queue_priority: u32,
80    /// `ze_device_properties_t::numThreadsPerEU`.
81    pub num_threads_per_eu: u32,
82    /// `ze_device_properties_t::physicalEUSimdWidth`.
83    pub physical_eu_simd_width: u32,
84    /// `ze_device_properties_t::numEUsPerSubslice`.
85    pub num_eus_per_subslice: u32,
86    /// `ze_device_properties_t::numSubslicesPerSlice`.
87    pub num_subslices_per_slice: u32,
88    /// `ze_device_properties_t::numSlices`.
89    pub num_slices: u32,
90    /// `ze_device_properties_t::timerResolution`.
91    pub timer_resolution: u64,
92    /// `ze_device_properties_t::timestampValidBits`.
93    pub timestamp_valid_bits: u32,
94    /// `ze_device_properties_t::kernelTimestampValidBits`.
95    pub kernel_timestamp_valid_bits: u32,
96    /// `ze_device_properties_t::uuid`.
97    pub uuid: ZeDeviceUuid,
98    /// `ze_device_properties_t::name`: NUL-terminated device name.
99    pub name: [core::ffi::c_char; ZE_MAX_DEVICE_NAME],
100}
101
102/// `ze_context_handle_t`.
103pub type ZeContextHandle = *mut c_void;
104/// `ze_command_queue_handle_t`.
105pub type ZeCommandQueueHandle = *mut c_void;
106/// `ze_command_list_handle_t`.
107pub type ZeCommandListHandle = *mut c_void;
108
109/// `ZE_STRUCTURE_TYPE_CONTEXT_DESC` value of `ze_structure_type_t`.
110pub const ZE_STRUCTURE_TYPE_CONTEXT_DESC: u32 = 0x2;
111/// `ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC` value of `ze_structure_type_t`.
112pub const ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC: u32 = 0x3;
113/// `ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC` value of `ze_structure_type_t`.
114pub const ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC: u32 = 0x4;
115/// `ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC` value of `ze_structure_type_t`.
116pub const ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC: u32 = 0xc;
117/// `ZE_STRUCTURE_TYPE_HOST_MEM_ALLOC_DESC` value of `ze_structure_type_t`.
118pub const ZE_STRUCTURE_TYPE_HOST_MEM_ALLOC_DESC: u32 = 0xd;
119/// `ZE_STRUCTURE_TYPE_MODULE_DESC` value of `ze_structure_type_t`.
120pub const ZE_STRUCTURE_TYPE_MODULE_DESC: u32 = 0xf;
121/// `ZE_STRUCTURE_TYPE_KERNEL_DESC` value of `ze_structure_type_t`.
122pub const ZE_STRUCTURE_TYPE_KERNEL_DESC: u32 = 0x10;
123
124/// `ZE_MODULE_FORMAT_IL_SPIRV` value of `ze_module_format_t`: SPIR-V input.
125pub const ZE_MODULE_FORMAT_IL_SPIRV: u32 = 0;
126/// `ZE_MODULE_FORMAT_NATIVE` value of `ze_module_format_t`: device-native binary.
127pub const ZE_MODULE_FORMAT_NATIVE: u32 = 1;
128
129/// `ze_module_handle_t`.
130pub type ZeModuleHandle = *mut c_void;
131/// `ze_kernel_handle_t`.
132pub type ZeKernelHandle = *mut c_void;
133
134/// Mirrors `ze_device_mem_alloc_desc_t`.
135#[repr(C)]
136pub struct ZeDeviceMemAllocDesc {
137    /// `ze_device_mem_alloc_desc_t::stype`.
138    pub stype: u32,
139    /// `ze_device_mem_alloc_desc_t::pNext`.
140    pub p_next: *const c_void,
141    /// `ze_device_mem_alloc_desc_t::flags`.
142    pub flags: u32,
143    /// `ze_device_mem_alloc_desc_t::ordinal`: device memory ordinal to allocate from.
144    pub ordinal: u32,
145}
146
147/// Mirrors `ze_host_mem_alloc_desc_t`.
148#[repr(C)]
149pub struct ZeHostMemAllocDesc {
150    /// `ze_host_mem_alloc_desc_t::stype`.
151    pub stype: u32,
152    /// `ze_host_mem_alloc_desc_t::pNext`.
153    pub p_next: *const c_void,
154    /// `ze_host_mem_alloc_desc_t::flags`.
155    pub flags: u32,
156}
157
158/// Mirrors `ze_module_desc_t`.
159#[repr(C)]
160pub struct ZeModuleDesc {
161    /// `ze_module_desc_t::stype`.
162    pub stype: u32,
163    /// `ze_module_desc_t::pNext`.
164    pub p_next: *const c_void,
165    /// `ze_module_desc_t::format`, one of the `ZE_MODULE_FORMAT_*` values.
166    pub format: u32,
167    /// `ze_module_desc_t::inputSize`, in bytes.
168    pub input_size: usize,
169    /// `ze_module_desc_t::pInputModule`.
170    pub p_input_module: *const u8,
171    /// `ze_module_desc_t::pBuildFlags`.
172    pub p_build_flags: *const core::ffi::c_char,
173    /// `ze_module_desc_t::pConstants`.
174    pub p_constants: *const c_void,
175}
176
177/// Mirrors `ze_kernel_desc_t`.
178#[repr(C)]
179pub struct ZeKernelDesc {
180    /// `ze_kernel_desc_t::stype`.
181    pub stype: u32,
182    /// `ze_kernel_desc_t::pNext`.
183    pub p_next: *const c_void,
184    /// `ze_kernel_desc_t::flags`.
185    pub flags: u32,
186    /// `ze_kernel_desc_t::pKernelName`.
187    pub p_kernel_name: *const core::ffi::c_char,
188}
189
190/// Mirrors `ze_group_count_t`.
191#[repr(C)]
192#[derive(Clone, Copy)]
193pub struct ZeGroupCount {
194    /// `ze_group_count_t::groupCountX`.
195    pub group_count_x: u32,
196    /// `ze_group_count_t::groupCountY`.
197    pub group_count_y: u32,
198    /// `ze_group_count_t::groupCountZ`.
199    pub group_count_z: u32,
200}
201
202/// `ZE_COMMAND_QUEUE_MODE_DEFAULT` value for `ze_command_queue_desc_t::mode`.
203pub const ZE_COMMAND_QUEUE_MODE_DEFAULT: u32 = 0;
204/// `ZE_COMMAND_QUEUE_PRIORITY_NORMAL` value for `ze_command_queue_desc_t::priority`.
205pub const ZE_COMMAND_QUEUE_PRIORITY_NORMAL: u32 = 0;
206
207/// Mirrors `ze_context_desc_t`.
208#[repr(C)]
209pub struct ZeContextDesc {
210    /// `ze_context_desc_t::stype`.
211    pub stype: u32,
212    /// `ze_context_desc_t::pNext`.
213    pub p_next: *const c_void,
214    /// `ze_context_desc_t::flags`.
215    pub flags: u32,
216}
217
218/// Mirrors `ze_command_queue_desc_t`.
219#[repr(C)]
220pub struct ZeCommandQueueDesc {
221    /// `ze_command_queue_desc_t::stype`.
222    pub stype: u32,
223    /// `ze_command_queue_desc_t::pNext`.
224    pub p_next: *const c_void,
225    /// `ze_command_queue_desc_t::ordinal`: command queue group ordinal.
226    pub ordinal: u32,
227    /// `ze_command_queue_desc_t::index`: queue index within the group.
228    pub index: u32,
229    /// `ze_command_queue_desc_t::flags`.
230    pub flags: u32,
231    /// `ze_command_queue_desc_t::mode`.
232    pub mode: u32,
233    /// `ze_command_queue_desc_t::priority`.
234    pub priority: u32,
235}
236
237/// Mirrors `ze_command_list_desc_t`.
238#[repr(C)]
239pub struct ZeCommandListDesc {
240    /// `ze_command_list_desc_t::stype`.
241    pub stype: u32,
242    /// `ze_command_list_desc_t::pNext`.
243    pub p_next: *const c_void,
244    /// `ze_command_list_desc_t::commandQueueGroupOrdinal`.
245    pub command_queue_group_ordinal: u32,
246    /// `ze_command_list_desc_t::flags`.
247    pub flags: u32,
248}
249
250type ZeInitFn = unsafe extern "C" fn(flags: u32) -> ZeResult;
251/// Function-pointer type of `zeContextCreate`.
252pub type ZeContextCreateFn = unsafe extern "C" fn(
253    h_driver: ZeDriverHandle,
254    desc: *const ZeContextDesc,
255    ph_context: *mut ZeContextHandle,
256) -> ZeResult;
257/// Function-pointer type of `zeContextDestroy`.
258pub type ZeContextDestroyFn = unsafe extern "C" fn(h_context: ZeContextHandle) -> ZeResult;
259/// Function-pointer type of `zeCommandQueueCreate`.
260pub type ZeCommandQueueCreateFn = unsafe extern "C" fn(
261    h_context: ZeContextHandle,
262    h_device: ZeDeviceHandle,
263    desc: *const ZeCommandQueueDesc,
264    ph_queue: *mut ZeCommandQueueHandle,
265) -> ZeResult;
266/// Function-pointer type of `zeCommandQueueDestroy`.
267pub type ZeCommandQueueDestroyFn = unsafe extern "C" fn(h_queue: ZeCommandQueueHandle) -> ZeResult;
268/// Function-pointer type of `zeCommandListCreate`.
269pub type ZeCommandListCreateFn = unsafe extern "C" fn(
270    h_context: ZeContextHandle,
271    h_device: ZeDeviceHandle,
272    desc: *const ZeCommandListDesc,
273    ph_list: *mut ZeCommandListHandle,
274) -> ZeResult;
275/// Function-pointer type of `zeCommandListDestroy`.
276pub type ZeCommandListDestroyFn = unsafe extern "C" fn(h_list: ZeCommandListHandle) -> ZeResult;
277type ZeDriverGetFn =
278    unsafe extern "C" fn(p_count: *mut u32, ph_drivers: *mut ZeDriverHandle) -> ZeResult;
279type ZeDeviceGetFn = unsafe extern "C" fn(
280    h_driver: ZeDriverHandle,
281    p_count: *mut u32,
282    ph_devices: *mut ZeDeviceHandle,
283) -> ZeResult;
284type ZeDeviceGetPropertiesFn = unsafe extern "C" fn(
285    h_device: ZeDeviceHandle,
286    p_properties: *mut ZeDeviceProperties,
287) -> ZeResult;
288
289/// Function-pointer type of `zeMemAllocDevice`.
290pub type ZeMemAllocDeviceFn = unsafe extern "C" fn(
291    h_context: ZeContextHandle,
292    device_desc: *const ZeDeviceMemAllocDesc,
293    size: usize,
294    alignment: usize,
295    h_device: ZeDeviceHandle,
296    pptr: *mut *mut c_void,
297) -> ZeResult;
298/// Function-pointer type of `zeMemAllocShared`.
299pub type ZeMemAllocSharedFn = unsafe extern "C" fn(
300    h_context: ZeContextHandle,
301    device_desc: *const ZeDeviceMemAllocDesc,
302    host_desc: *const ZeHostMemAllocDesc,
303    size: usize,
304    alignment: usize,
305    h_device: ZeDeviceHandle,
306    pptr: *mut *mut c_void,
307) -> ZeResult;
308/// Function-pointer type of `zeMemFree`.
309pub type ZeMemFreeFn =
310    unsafe extern "C" fn(h_context: ZeContextHandle, ptr: *mut c_void) -> ZeResult;
311/// Function-pointer type of `zeModuleCreate`.
312pub type ZeModuleCreateFn = unsafe extern "C" fn(
313    h_context: ZeContextHandle,
314    h_device: ZeDeviceHandle,
315    desc: *const ZeModuleDesc,
316    ph_module: *mut ZeModuleHandle,
317    ph_build_log: *mut *mut c_void,
318) -> ZeResult;
319/// Function-pointer type of `zeModuleDestroy`.
320pub type ZeModuleDestroyFn = unsafe extern "C" fn(h_module: ZeModuleHandle) -> ZeResult;
321/// Function-pointer type of `zeKernelCreate`.
322pub type ZeKernelCreateFn = unsafe extern "C" fn(
323    h_module: ZeModuleHandle,
324    desc: *const ZeKernelDesc,
325    ph_kernel: *mut ZeKernelHandle,
326) -> ZeResult;
327/// Function-pointer type of `zeKernelDestroy`.
328pub type ZeKernelDestroyFn = unsafe extern "C" fn(h_kernel: ZeKernelHandle) -> ZeResult;
329/// Function-pointer type of `zeKernelSetGroupSize`.
330pub type ZeKernelSetGroupSizeFn =
331    unsafe extern "C" fn(h_kernel: ZeKernelHandle, gx: u32, gy: u32, gz: u32) -> ZeResult;
332/// Function-pointer type of `zeKernelSetArgumentValue`.
333pub type ZeKernelSetArgumentValueFn = unsafe extern "C" fn(
334    h_kernel: ZeKernelHandle,
335    arg_index: u32,
336    arg_size: usize,
337    p_arg_value: *const c_void,
338) -> ZeResult;
339/// Function-pointer type of `zeCommandListAppendLaunchKernel`.
340pub type ZeCommandListAppendLaunchKernelFn = unsafe extern "C" fn(
341    h_list: ZeCommandListHandle,
342    h_kernel: ZeKernelHandle,
343    p_launch_args: *const ZeGroupCount,
344    h_signal_event: *mut c_void,
345    num_wait_events: u32,
346    ph_wait_events: *mut *mut c_void,
347) -> ZeResult;
348/// Function-pointer type of `zeCommandListAppendMemoryCopy`.
349pub type ZeCommandListAppendMemoryCopyFn = unsafe extern "C" fn(
350    h_list: ZeCommandListHandle,
351    dstptr: *mut c_void,
352    srcptr: *const c_void,
353    size: usize,
354    h_signal_event: *mut c_void,
355    num_wait_events: u32,
356    ph_wait_events: *mut *mut c_void,
357) -> ZeResult;
358/// Function-pointer type of `zeCommandListClose`.
359pub type ZeCommandListCloseFn = unsafe extern "C" fn(h_list: ZeCommandListHandle) -> ZeResult;
360/// Function-pointer type of `zeCommandListReset`.
361pub type ZeCommandListResetFn = unsafe extern "C" fn(h_list: ZeCommandListHandle) -> ZeResult;
362/// Function-pointer type of `zeCommandQueueExecuteCommandLists`.
363pub type ZeCommandQueueExecuteCommandListsFn = unsafe extern "C" fn(
364    h_queue: ZeCommandQueueHandle,
365    num_lists: u32,
366    ph_lists: *const ZeCommandListHandle,
367    h_fence: *mut c_void,
368) -> ZeResult;
369/// Function-pointer type of `zeCommandQueueSynchronize`.
370pub type ZeCommandQueueSynchronizeFn =
371    unsafe extern "C" fn(h_queue: ZeCommandQueueHandle, timeout: u64) -> ZeResult;
372
373// ── Loader ─────────────────────────────────────────────────────────────────
374
375static LIB: OnceCell<Option<Loaded>> = OnceCell::new();
376
377/// Level Zero entry points resolved from `ze_loader`, kept alive for the process lifetime.
378pub struct Loaded {
379    _lib: Library,
380    /// `zeDriverGet`.
381    pub ze_driver_get: ZeDriverGetFn,
382    /// `zeDeviceGet`.
383    pub ze_device_get: ZeDeviceGetFn,
384    /// `zeDeviceGetProperties`.
385    pub ze_device_get_properties: ZeDeviceGetPropertiesFn,
386    /// `zeContextCreate`.
387    pub ze_context_create: ZeContextCreateFn,
388    /// `zeContextDestroy`.
389    pub ze_context_destroy: ZeContextDestroyFn,
390    /// `zeCommandQueueCreate`.
391    pub ze_command_queue_create: ZeCommandQueueCreateFn,
392    /// `zeCommandQueueDestroy`.
393    pub ze_command_queue_destroy: ZeCommandQueueDestroyFn,
394    /// `zeCommandListCreate`.
395    pub ze_command_list_create: ZeCommandListCreateFn,
396    /// `zeCommandListDestroy`.
397    pub ze_command_list_destroy: ZeCommandListDestroyFn,
398    /// `zeMemAllocDevice`.
399    pub ze_mem_alloc_device: ZeMemAllocDeviceFn,
400    /// `zeMemAllocShared`.
401    pub ze_mem_alloc_shared: ZeMemAllocSharedFn,
402    /// `zeMemFree`.
403    pub ze_mem_free: ZeMemFreeFn,
404    /// `zeModuleCreate`.
405    pub ze_module_create: ZeModuleCreateFn,
406    /// `zeModuleDestroy`.
407    pub ze_module_destroy: ZeModuleDestroyFn,
408    /// `zeKernelCreate`.
409    pub ze_kernel_create: ZeKernelCreateFn,
410    /// `zeKernelDestroy`.
411    pub ze_kernel_destroy: ZeKernelDestroyFn,
412    /// `zeKernelSetGroupSize`.
413    pub ze_kernel_set_group_size: ZeKernelSetGroupSizeFn,
414    /// `zeKernelSetArgumentValue`.
415    pub ze_kernel_set_argument_value: ZeKernelSetArgumentValueFn,
416    /// `zeCommandListAppendLaunchKernel`.
417    pub ze_command_list_append_launch_kernel: ZeCommandListAppendLaunchKernelFn,
418    /// `zeCommandListAppendMemoryCopy`.
419    pub ze_command_list_append_memory_copy: ZeCommandListAppendMemoryCopyFn,
420    /// `zeCommandListClose`.
421    pub ze_command_list_close: ZeCommandListCloseFn,
422    /// `zeCommandListReset`.
423    pub ze_command_list_reset: ZeCommandListResetFn,
424    /// `zeCommandQueueExecuteCommandLists`.
425    pub ze_command_queue_execute_command_lists: ZeCommandQueueExecuteCommandListsFn,
426    /// `zeCommandQueueSynchronize`.
427    pub ze_command_queue_synchronize: ZeCommandQueueSynchronizeFn,
428}
429
430unsafe impl Send for Loaded {}
431unsafe impl Sync for Loaded {}
432
433#[cfg(target_os = "windows")]
434const LIB_CANDIDATES: &[&str] = &["ze_loader.dll"];
435#[cfg(any(target_os = "linux", target_os = "android"))]
436const LIB_CANDIDATES: &[&str] = &["libze_loader.so.1", "libze_loader.so"];
437#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "android")))]
438const LIB_CANDIDATES: &[&str] = &[];
439
440pub(crate) fn loaded() -> Option<&'static Loaded> {
441    LIB.get_or_init(load).as_ref()
442}
443
444fn load() -> Option<Loaded> {
445    for name in LIB_CANDIDATES {
446        let lib = match unsafe { Library::new(*name) } {
447            Ok(l) => l,
448            Err(_) => continue,
449        };
450        unsafe {
451            let init: Symbol<ZeInitFn> = match lib.get(b"zeInit\0") {
452                Ok(s) => s,
453                Err(_) => continue,
454            };
455            if init(0) != ZE_RESULT_SUCCESS {
456                continue;
457            }
458            let dg: Symbol<ZeDriverGetFn> = match lib.get(b"zeDriverGet\0") {
459                Ok(s) => s,
460                Err(_) => continue,
461            };
462            let devg: Symbol<ZeDeviceGetFn> = match lib.get(b"zeDeviceGet\0") {
463                Ok(s) => s,
464                Err(_) => continue,
465            };
466            let devp: Symbol<ZeDeviceGetPropertiesFn> = match lib.get(b"zeDeviceGetProperties\0") {
467                Ok(s) => s,
468                Err(_) => continue,
469            };
470            let ze_driver_get = *dg;
471            let ze_device_get = *devg;
472            let ze_device_get_properties = *devp;
473            drop(dg);
474            drop(devg);
475            drop(devp);
476            drop(init);
477
478            let ctx_create: Symbol<ZeContextCreateFn> = match lib.get(b"zeContextCreate\0") {
479                Ok(s) => s,
480                Err(_) => continue,
481            };
482            let ctx_destroy: Symbol<ZeContextDestroyFn> = match lib.get(b"zeContextDestroy\0") {
483                Ok(s) => s,
484                Err(_) => continue,
485            };
486            let q_create: Symbol<ZeCommandQueueCreateFn> = match lib.get(b"zeCommandQueueCreate\0")
487            {
488                Ok(s) => s,
489                Err(_) => continue,
490            };
491            let q_destroy: Symbol<ZeCommandQueueDestroyFn> =
492                match lib.get(b"zeCommandQueueDestroy\0") {
493                    Ok(s) => s,
494                    Err(_) => continue,
495                };
496            let l_create: Symbol<ZeCommandListCreateFn> = match lib.get(b"zeCommandListCreate\0") {
497                Ok(s) => s,
498                Err(_) => continue,
499            };
500            let l_destroy: Symbol<ZeCommandListDestroyFn> = match lib.get(b"zeCommandListDestroy\0")
501            {
502                Ok(s) => s,
503                Err(_) => continue,
504            };
505
506            let ze_context_create = *ctx_create;
507            let ze_context_destroy = *ctx_destroy;
508            let ze_command_queue_create = *q_create;
509            let ze_command_queue_destroy = *q_destroy;
510            let ze_command_list_create = *l_create;
511            let ze_command_list_destroy = *l_destroy;
512            drop(ctx_create);
513            drop(ctx_destroy);
514            drop(q_create);
515            drop(q_destroy);
516            drop(l_create);
517            drop(l_destroy);
518
519            macro_rules! sym {
520                ($ty:ty, $name:literal) => {{
521                    let s: Symbol<$ty> = match lib.get($name) {
522                        Ok(s) => s,
523                        Err(_) => continue,
524                    };
525                    let f = *s;
526                    drop(s);
527                    f
528                }};
529            }
530            let ze_mem_alloc_device = sym!(ZeMemAllocDeviceFn, b"zeMemAllocDevice\0");
531            let ze_mem_alloc_shared = sym!(ZeMemAllocSharedFn, b"zeMemAllocShared\0");
532            let ze_mem_free = sym!(ZeMemFreeFn, b"zeMemFree\0");
533            let ze_module_create = sym!(ZeModuleCreateFn, b"zeModuleCreate\0");
534            let ze_module_destroy = sym!(ZeModuleDestroyFn, b"zeModuleDestroy\0");
535            let ze_kernel_create = sym!(ZeKernelCreateFn, b"zeKernelCreate\0");
536            let ze_kernel_destroy = sym!(ZeKernelDestroyFn, b"zeKernelDestroy\0");
537            let ze_kernel_set_group_size = sym!(ZeKernelSetGroupSizeFn, b"zeKernelSetGroupSize\0");
538            let ze_kernel_set_argument_value =
539                sym!(ZeKernelSetArgumentValueFn, b"zeKernelSetArgumentValue\0");
540            let ze_command_list_append_launch_kernel = sym!(
541                ZeCommandListAppendLaunchKernelFn,
542                b"zeCommandListAppendLaunchKernel\0"
543            );
544            let ze_command_list_append_memory_copy = sym!(
545                ZeCommandListAppendMemoryCopyFn,
546                b"zeCommandListAppendMemoryCopy\0"
547            );
548            let ze_command_list_close = sym!(ZeCommandListCloseFn, b"zeCommandListClose\0");
549            let ze_command_list_reset = sym!(ZeCommandListResetFn, b"zeCommandListReset\0");
550            let ze_command_queue_execute_command_lists = sym!(
551                ZeCommandQueueExecuteCommandListsFn,
552                b"zeCommandQueueExecuteCommandLists\0"
553            );
554            let ze_command_queue_synchronize =
555                sym!(ZeCommandQueueSynchronizeFn, b"zeCommandQueueSynchronize\0");
556
557            return Some(Loaded {
558                _lib: lib,
559                ze_driver_get,
560                ze_device_get,
561                ze_device_get_properties,
562                ze_context_create,
563                ze_context_destroy,
564                ze_command_queue_create,
565                ze_command_queue_destroy,
566                ze_command_list_create,
567                ze_command_list_destroy,
568                ze_mem_alloc_device,
569                ze_mem_alloc_shared,
570                ze_mem_free,
571                ze_module_create,
572                ze_module_destroy,
573                ze_kernel_create,
574                ze_kernel_destroy,
575                ze_kernel_set_group_size,
576                ze_kernel_set_argument_value,
577                ze_command_list_append_launch_kernel,
578                ze_command_list_append_memory_copy,
579                ze_command_list_close,
580                ze_command_list_reset,
581                ze_command_queue_execute_command_lists,
582                ze_command_queue_synchronize,
583            });
584        }
585    }
586    None
587}
588
589// ── Enumeration ────────────────────────────────────────────────────────────
590
591/// Device record flattened from `ze_device_properties_t` by [`enumerate`].
592#[derive(Debug, Clone)]
593pub struct EnumeratedDevice {
594    /// Global device ordinal across all drivers, in enumeration order.
595    pub ordinal: u32,
596    /// `ze_device_type_t` of the device.
597    pub type_: u32,
598    /// PCI vendor ID.
599    pub vendor_id: u32,
600    /// PCI device ID.
601    pub device_id: u32,
602    /// Device name, decoded from `ze_device_properties_t::name`.
603    pub name: String,
604    /// Core clock in kHz, converted from `coreClockRate` (MHz).
605    pub core_clock_khz: u32,
606    /// Largest single allocation in bytes.
607    pub max_mem_alloc_size: u64,
608    /// Number of slices.
609    pub num_slices: u32,
610    /// Number of subslices per slice.
611    pub num_subslices_per_slice: u32,
612    /// Number of execution units per subslice.
613    pub num_eus_per_subslice: u32,
614}
615
616/// Whether `ze_loader` loaded and `zeInit` succeeded.
617pub fn is_available() -> bool {
618    loaded().is_some()
619}
620
621/// Lists every device across all Level Zero drivers, in driver walk order.
622pub fn enumerate() -> Vec<EnumeratedDevice> {
623    let Some(l) = loaded() else { return Vec::new() };
624    let mut out = Vec::new();
625    let mut ordinal = 0u32;
626    unsafe {
627        let mut driver_count: u32 = 0;
628        if (l.ze_driver_get)(&mut driver_count, core::ptr::null_mut()) != ZE_RESULT_SUCCESS
629            || driver_count == 0
630        {
631            return out;
632        }
633        let mut drivers = vec![core::ptr::null_mut::<c_void>(); driver_count as usize];
634        if (l.ze_driver_get)(&mut driver_count, drivers.as_mut_ptr()) != ZE_RESULT_SUCCESS {
635            return out;
636        }
637        for driver in drivers.into_iter().take(driver_count as usize) {
638            let mut dev_count: u32 = 0;
639            if (l.ze_device_get)(driver, &mut dev_count, core::ptr::null_mut()) != ZE_RESULT_SUCCESS
640                || dev_count == 0
641            {
642                continue;
643            }
644            let mut devs = vec![core::ptr::null_mut::<c_void>(); dev_count as usize];
645            if (l.ze_device_get)(driver, &mut dev_count, devs.as_mut_ptr()) != ZE_RESULT_SUCCESS {
646                continue;
647            }
648            for dev in devs.into_iter().take(dev_count as usize) {
649                let mut props: ZeDeviceProperties = core::mem::zeroed();
650                props.stype = ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES;
651                if (l.ze_device_get_properties)(dev, &mut props) != ZE_RESULT_SUCCESS {
652                    continue;
653                }
654                out.push(EnumeratedDevice {
655                    ordinal,
656                    type_: props.type_,
657                    vendor_id: props.vendor_id,
658                    device_id: props.device_id,
659                    name: c_name_to_string(&props.name),
660                    core_clock_khz: props.core_clock_rate.saturating_mul(1000),
661                    max_mem_alloc_size: props.max_mem_alloc_size,
662                    num_slices: props.num_slices,
663                    num_subslices_per_slice: props.num_subslices_per_slice,
664                    num_eus_per_subslice: props.num_eus_per_subslice,
665                });
666                ordinal += 1;
667            }
668        }
669    }
670    out
671}
672
673fn c_name_to_string(raw: &[core::ffi::c_char]) -> String {
674    let bytes: Vec<u8> = raw
675        .iter()
676        .take_while(|&&c| c != 0)
677        .map(|&c| c as u8)
678        .collect();
679    String::from_utf8_lossy(&bytes).into_owned()
680}
681
682#[cfg(test)]
683mod tests {
684    use super::*;
685
686    #[test]
687    fn probe_does_not_panic() {
688        let _ = is_available();
689        let _ = enumerate();
690    }
691}