1#![allow(clippy::drop_non_drop)] use core::ffi::c_void;
16use libloading::{Library, Symbol};
17use once_cell::sync::OnceCell;
18
19pub type ZeResult = u32;
23pub const ZE_RESULT_SUCCESS: ZeResult = 0;
25
26pub type ZeDriverHandle = *mut c_void;
28pub type ZeDeviceHandle = *mut c_void;
30
31pub const ZE_STRUCTURE_TYPE_DEVICE_PROPERTIES: u32 = 0x1;
33
34pub const ZE_DEVICE_TYPE_GPU: u32 = 1;
36pub const ZE_DEVICE_TYPE_CPU: u32 = 2;
38pub const ZE_DEVICE_TYPE_FPGA: u32 = 3;
40pub const ZE_DEVICE_TYPE_MCA: u32 = 4;
42pub const ZE_DEVICE_TYPE_VPU: u32 = 5;
44
45pub const ZE_MAX_DEVICE_NAME: usize = 256;
47
48#[repr(C)]
50#[derive(Clone, Copy)]
51pub struct ZeDeviceUuid {
52 pub id: [u8; 16],
54}
55
56#[repr(C)]
58#[derive(Clone, Copy)]
59pub struct ZeDeviceProperties {
60 pub stype: u32,
62 pub p_next: *mut c_void,
64 pub type_: u32,
66 pub vendor_id: u32,
68 pub device_id: u32,
70 pub subdevice_id: u32,
72 pub core_clock_rate: u32,
74 pub max_mem_alloc_size: u64,
76 pub max_hardware_contexts: u32,
78 pub max_command_queue_priority: u32,
80 pub num_threads_per_eu: u32,
82 pub physical_eu_simd_width: u32,
84 pub num_eus_per_subslice: u32,
86 pub num_subslices_per_slice: u32,
88 pub num_slices: u32,
90 pub timer_resolution: u64,
92 pub timestamp_valid_bits: u32,
94 pub kernel_timestamp_valid_bits: u32,
96 pub uuid: ZeDeviceUuid,
98 pub name: [core::ffi::c_char; ZE_MAX_DEVICE_NAME],
100}
101
102pub type ZeContextHandle = *mut c_void;
104pub type ZeCommandQueueHandle = *mut c_void;
106pub type ZeCommandListHandle = *mut c_void;
108
109pub const ZE_STRUCTURE_TYPE_CONTEXT_DESC: u32 = 0x2;
111pub const ZE_STRUCTURE_TYPE_COMMAND_QUEUE_DESC: u32 = 0x3;
113pub const ZE_STRUCTURE_TYPE_COMMAND_LIST_DESC: u32 = 0x4;
115pub const ZE_STRUCTURE_TYPE_DEVICE_MEM_ALLOC_DESC: u32 = 0xc;
117pub const ZE_STRUCTURE_TYPE_HOST_MEM_ALLOC_DESC: u32 = 0xd;
119pub const ZE_STRUCTURE_TYPE_MODULE_DESC: u32 = 0xf;
121pub const ZE_STRUCTURE_TYPE_KERNEL_DESC: u32 = 0x10;
123
124pub const ZE_MODULE_FORMAT_IL_SPIRV: u32 = 0;
126pub const ZE_MODULE_FORMAT_NATIVE: u32 = 1;
128
129pub type ZeModuleHandle = *mut c_void;
131pub type ZeKernelHandle = *mut c_void;
133
134#[repr(C)]
136pub struct ZeDeviceMemAllocDesc {
137 pub stype: u32,
139 pub p_next: *const c_void,
141 pub flags: u32,
143 pub ordinal: u32,
145}
146
147#[repr(C)]
149pub struct ZeHostMemAllocDesc {
150 pub stype: u32,
152 pub p_next: *const c_void,
154 pub flags: u32,
156}
157
158#[repr(C)]
160pub struct ZeModuleDesc {
161 pub stype: u32,
163 pub p_next: *const c_void,
165 pub format: u32,
167 pub input_size: usize,
169 pub p_input_module: *const u8,
171 pub p_build_flags: *const core::ffi::c_char,
173 pub p_constants: *const c_void,
175}
176
177#[repr(C)]
179pub struct ZeKernelDesc {
180 pub stype: u32,
182 pub p_next: *const c_void,
184 pub flags: u32,
186 pub p_kernel_name: *const core::ffi::c_char,
188}
189
190#[repr(C)]
192#[derive(Clone, Copy)]
193pub struct ZeGroupCount {
194 pub group_count_x: u32,
196 pub group_count_y: u32,
198 pub group_count_z: u32,
200}
201
202pub const ZE_COMMAND_QUEUE_MODE_DEFAULT: u32 = 0;
204pub const ZE_COMMAND_QUEUE_PRIORITY_NORMAL: u32 = 0;
206
207#[repr(C)]
209pub struct ZeContextDesc {
210 pub stype: u32,
212 pub p_next: *const c_void,
214 pub flags: u32,
216}
217
218#[repr(C)]
220pub struct ZeCommandQueueDesc {
221 pub stype: u32,
223 pub p_next: *const c_void,
225 pub ordinal: u32,
227 pub index: u32,
229 pub flags: u32,
231 pub mode: u32,
233 pub priority: u32,
235}
236
237#[repr(C)]
239pub struct ZeCommandListDesc {
240 pub stype: u32,
242 pub p_next: *const c_void,
244 pub command_queue_group_ordinal: u32,
246 pub flags: u32,
248}
249
250type ZeInitFn = unsafe extern "C" fn(flags: u32) -> ZeResult;
251pub type ZeContextCreateFn = unsafe extern "C" fn(
253 h_driver: ZeDriverHandle,
254 desc: *const ZeContextDesc,
255 ph_context: *mut ZeContextHandle,
256) -> ZeResult;
257pub type ZeContextDestroyFn = unsafe extern "C" fn(h_context: ZeContextHandle) -> ZeResult;
259pub 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;
266pub type ZeCommandQueueDestroyFn = unsafe extern "C" fn(h_queue: ZeCommandQueueHandle) -> ZeResult;
268pub 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;
275pub 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
289pub 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;
298pub 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;
308pub type ZeMemFreeFn =
310 unsafe extern "C" fn(h_context: ZeContextHandle, ptr: *mut c_void) -> ZeResult;
311pub 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;
319pub type ZeModuleDestroyFn = unsafe extern "C" fn(h_module: ZeModuleHandle) -> ZeResult;
321pub type ZeKernelCreateFn = unsafe extern "C" fn(
323 h_module: ZeModuleHandle,
324 desc: *const ZeKernelDesc,
325 ph_kernel: *mut ZeKernelHandle,
326) -> ZeResult;
327pub type ZeKernelDestroyFn = unsafe extern "C" fn(h_kernel: ZeKernelHandle) -> ZeResult;
329pub type ZeKernelSetGroupSizeFn =
331 unsafe extern "C" fn(h_kernel: ZeKernelHandle, gx: u32, gy: u32, gz: u32) -> ZeResult;
332pub 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;
339pub 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;
348pub 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;
358pub type ZeCommandListCloseFn = unsafe extern "C" fn(h_list: ZeCommandListHandle) -> ZeResult;
360pub type ZeCommandListResetFn = unsafe extern "C" fn(h_list: ZeCommandListHandle) -> ZeResult;
362pub 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;
369pub type ZeCommandQueueSynchronizeFn =
371 unsafe extern "C" fn(h_queue: ZeCommandQueueHandle, timeout: u64) -> ZeResult;
372
373static LIB: OnceCell<Option<Loaded>> = OnceCell::new();
376
377pub struct Loaded {
379 _lib: Library,
380 pub ze_driver_get: ZeDriverGetFn,
382 pub ze_device_get: ZeDeviceGetFn,
384 pub ze_device_get_properties: ZeDeviceGetPropertiesFn,
386 pub ze_context_create: ZeContextCreateFn,
388 pub ze_context_destroy: ZeContextDestroyFn,
390 pub ze_command_queue_create: ZeCommandQueueCreateFn,
392 pub ze_command_queue_destroy: ZeCommandQueueDestroyFn,
394 pub ze_command_list_create: ZeCommandListCreateFn,
396 pub ze_command_list_destroy: ZeCommandListDestroyFn,
398 pub ze_mem_alloc_device: ZeMemAllocDeviceFn,
400 pub ze_mem_alloc_shared: ZeMemAllocSharedFn,
402 pub ze_mem_free: ZeMemFreeFn,
404 pub ze_module_create: ZeModuleCreateFn,
406 pub ze_module_destroy: ZeModuleDestroyFn,
408 pub ze_kernel_create: ZeKernelCreateFn,
410 pub ze_kernel_destroy: ZeKernelDestroyFn,
412 pub ze_kernel_set_group_size: ZeKernelSetGroupSizeFn,
414 pub ze_kernel_set_argument_value: ZeKernelSetArgumentValueFn,
416 pub ze_command_list_append_launch_kernel: ZeCommandListAppendLaunchKernelFn,
418 pub ze_command_list_append_memory_copy: ZeCommandListAppendMemoryCopyFn,
420 pub ze_command_list_close: ZeCommandListCloseFn,
422 pub ze_command_list_reset: ZeCommandListResetFn,
424 pub ze_command_queue_execute_command_lists: ZeCommandQueueExecuteCommandListsFn,
426 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#[derive(Debug, Clone)]
593pub struct EnumeratedDevice {
594 pub ordinal: u32,
596 pub type_: u32,
598 pub vendor_id: u32,
600 pub device_id: u32,
602 pub name: String,
604 pub core_clock_khz: u32,
606 pub max_mem_alloc_size: u64,
608 pub num_slices: u32,
610 pub num_subslices_per_slice: u32,
612 pub num_eus_per_subslice: u32,
614}
615
616pub fn is_available() -> bool {
618 loaded().is_some()
619}
620
621pub 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}