use crate::{check, sys, Result};
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr;
fn ia() -> Result<&'static sys::InteropApi> {
crate::interop_api().ok_or_else(|| crate::Error::new(-1, "InteropApi unavailable"))
}
fn ia_fn<T: Copy>(f: Option<T>, function_name: &str) -> Result<T> {
crate::model_editor::require_sub_api_fn(f, "InteropApi", function_name)
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExternalMemoryHandleType {
D3D12Resource = 0,
D3D12Heap = 1,
VkMemoryWin32 = 2,
VkMemoryOpaqueFd = 3,
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExternalSemaphoreType {
D3D12Fence = 0,
VkTimelineSemaphoreWin32 = 1,
VkTimelineSemaphoreOpaqueFd = 2,
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum GraphicsApi {
#[default]
None = 0,
D3D12 = 1,
Vulkan = 2,
}
#[repr(C)]
pub struct ExternalMemoryDescriptor {
pub version: u32,
pub handle_type: ExternalMemoryHandleType,
pub native_handle: *mut c_void,
pub size_bytes: usize,
pub offset_bytes: usize,
}
const _: () = assert!(std::mem::size_of::<ExternalMemoryDescriptor>() == 32);
impl ExternalMemoryDescriptor {
pub fn new(
handle_type: ExternalMemoryHandleType, native_handle: *mut c_void, size_bytes: usize,
) -> Self {
Self {
version: sys::API_VERSION,
handle_type,
native_handle,
size_bytes,
offset_bytes: 0,
}
}
}
#[repr(C)]
pub struct ExternalSemaphoreDescriptor {
pub version: u32,
pub semaphore_type: ExternalSemaphoreType,
pub native_handle: *mut c_void,
}
const _: () = assert!(std::mem::size_of::<ExternalSemaphoreDescriptor>() == 16);
impl ExternalSemaphoreDescriptor {
pub fn new(semaphore_type: ExternalSemaphoreType, native_handle: *mut c_void) -> Self {
Self {
version: sys::API_VERSION,
semaphore_type,
native_handle,
}
}
}
#[repr(C)]
pub struct ExternalTensorDescriptor<'a> {
pub version: u32,
pub element_type: sys::ElementType,
pub shape: *const i64,
pub rank: usize,
pub offset_bytes: usize,
_shape: PhantomData<&'a [i64]>,
}
const _: () = assert!(std::mem::size_of::<ExternalTensorDescriptor<'static>>() == 32);
impl<'a> ExternalTensorDescriptor<'a> {
pub fn new(element_type: sys::ElementType, shape: &'a [i64]) -> Self {
Self {
version: sys::API_VERSION,
element_type,
shape: shape.as_ptr(),
rank: shape.len(),
offset_bytes: 0,
_shape: PhantomData,
}
}
}
#[repr(C)]
pub struct GraphicsInteropConfig {
pub version: u32,
pub graphics_api: GraphicsApi,
pub command_queue: *mut c_void,
pub additional_options: *const c_void,
}
const _: () = assert!(std::mem::size_of::<GraphicsInteropConfig>() == 24);
impl GraphicsInteropConfig {
pub fn new(graphics_api: GraphicsApi, command_queue: *mut c_void) -> Self {
Self {
version: sys::API_VERSION,
graphics_api,
command_queue,
additional_options: ptr::null(),
}
}
}
pub struct ExternalMemoryHandle {
ptr: *mut sys::ExternalMemoryHandleHandle,
}
impl Drop for ExternalMemoryHandle {
fn drop(&mut self) {
if let Some(ia) = crate::interop_api() {
if let Some(release) = ia.ReleaseExternalMemoryHandle {
unsafe { release(self.ptr) };
}
}
}
}
pub struct ExternalSemaphoreHandle {
ptr: *mut sys::ExternalSemaphoreHandleHandle,
}
impl Drop for ExternalSemaphoreHandle {
fn drop(&mut self) {
if let Some(ia) = crate::interop_api() {
if let Some(release) = ia.ReleaseExternalSemaphoreHandle {
unsafe { release(self.ptr) };
}
}
}
}
pub struct ExternalResourceImporter {
ptr: *mut sys::ExternalResourceImporterHandle,
}
impl ExternalResourceImporter {
pub unsafe fn for_device(ep_device: *const sys::EpDeviceHandle) -> Result<Option<Self>> {
let create = ia_fn(
ia()?.CreateExternalResourceImporterForDevice,
"CreateExternalResourceImporterForDevice",
)?;
let mut p: *mut sys::ExternalResourceImporterHandle = ptr::null_mut();
check(unsafe { create(ep_device, &mut p) })?;
Ok(if p.is_null() {
None
} else {
Some(Self { ptr: p })
})
}
pub fn can_import_memory(&self, handle_type: ExternalMemoryHandleType) -> Result<bool> {
let mut supported = false;
let f: unsafe extern "C" fn(
*const sys::ExternalResourceImporterHandle,
i32,
*mut bool,
) -> sys::StatusPtr =
unsafe { std::mem::transmute(ia_fn(ia()?.CanImportMemory, "CanImportMemory")?) };
check(unsafe { f(self.ptr, handle_type as i32, &mut supported) })?;
Ok(supported)
}
pub fn import_memory(&self, desc: &ExternalMemoryDescriptor) -> Result<ExternalMemoryHandle> {
let import = ia_fn(ia()?.ImportMemory, "ImportMemory")?;
let mut p: *mut sys::ExternalMemoryHandleHandle = ptr::null_mut();
check(unsafe {
import(
self.ptr,
desc as *const ExternalMemoryDescriptor
as *const sys::ExternalMemoryDescriptorHandle,
&mut p,
)
})?;
let p = crate::ensure_non_null(p, "external memory handle")?;
Ok(ExternalMemoryHandle { ptr: p })
}
pub fn create_tensor_from_memory(
&self, mem_handle: &ExternalMemoryHandle, tensor_desc: &ExternalTensorDescriptor<'_>,
) -> Result<crate::OwnedValue> {
let create = ia_fn(ia()?.CreateTensorFromMemory, "CreateTensorFromMemory")?;
let mut p: *mut sys::ValueHandle = ptr::null_mut();
check(unsafe {
create(
self.ptr,
mem_handle.ptr,
tensor_desc as *const ExternalTensorDescriptor
as *const sys::ExternalTensorDescriptorHandle,
&mut p,
)
})?;
let p = crate::ensure_non_null(p, "external tensor value")?;
crate::OwnedValue::from_introspect(p)
}
pub fn can_import_semaphore(&self, semaphore_type: ExternalSemaphoreType) -> Result<bool> {
let mut supported = false;
let f: unsafe extern "C" fn(
*const sys::ExternalResourceImporterHandle,
i32,
*mut bool,
) -> sys::StatusPtr =
unsafe { std::mem::transmute(ia_fn(ia()?.CanImportSemaphore, "CanImportSemaphore")?) };
check(unsafe { f(self.ptr, semaphore_type as i32, &mut supported) })?;
Ok(supported)
}
pub fn import_semaphore(
&self, desc: &ExternalSemaphoreDescriptor,
) -> Result<ExternalSemaphoreHandle> {
let import = ia_fn(ia()?.ImportSemaphore, "ImportSemaphore")?;
let mut p: *mut sys::ExternalSemaphoreHandleHandle = ptr::null_mut();
check(unsafe {
import(
self.ptr,
desc as *const ExternalSemaphoreDescriptor
as *const sys::ExternalSemaphoreDescriptorHandle,
&mut p,
)
})?;
let p = crate::ensure_non_null(p, "external semaphore handle")?;
Ok(ExternalSemaphoreHandle { ptr: p })
}
pub unsafe fn wait_semaphore(
&self, semaphore: &ExternalSemaphoreHandle, stream: *mut sys::SyncStreamHandle, value: u64,
) -> Result<()> {
let wait = ia_fn(ia()?.WaitSemaphore, "WaitSemaphore")?;
check(unsafe { wait(self.ptr, semaphore.ptr, stream, value) })
}
pub unsafe fn signal_semaphore(
&self, semaphore: &ExternalSemaphoreHandle, stream: *mut sys::SyncStreamHandle, value: u64,
) -> Result<()> {
let signal = ia_fn(ia()?.SignalSemaphore, "SignalSemaphore")?;
check(unsafe { signal(self.ptr, semaphore.ptr, stream, value) })
}
}
impl Drop for ExternalResourceImporter {
fn drop(&mut self) {
if let Some(ia) = crate::interop_api() {
if let Some(release) = ia.ReleaseExternalResourceImporter {
unsafe { release(self.ptr) };
}
}
}
}
pub unsafe fn init_graphics_interop_for_ep_device(
ep_device: *const sys::EpDeviceHandle, config: &GraphicsInteropConfig,
) -> Result<()> {
let init = ia_fn(
ia()?.InitGraphicsInteropForEpDevice,
"InitGraphicsInteropForEpDevice",
)?;
check(unsafe {
init(
ep_device,
config as *const GraphicsInteropConfig as *const sys::GraphicsInteropConfigHandle,
)
})
}
pub unsafe fn deinit_graphics_interop_for_ep_device(
ep_device: *const sys::EpDeviceHandle,
) -> Result<()> {
let deinit = ia_fn(
ia()?.DeinitGraphicsInteropForEpDevice,
"DeinitGraphicsInteropForEpDevice",
)?;
check(unsafe { deinit(ep_device) })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn interop_gateway_and_descriptors() {
let ia = ia().expect("InteropApi populated");
unsafe {
ia_fn(
ia.ReleaseExternalResourceImporter,
"ReleaseExternalResourceImporter",
)
.expect("ReleaseExternalResourceImporter")(ptr::null_mut());
ia_fn(
ia.ReleaseExternalMemoryHandle,
"ReleaseExternalMemoryHandle",
)
.expect("ReleaseExternalMemoryHandle")(ptr::null_mut());
ia_fn(
ia.ReleaseExternalSemaphoreHandle,
"ReleaseExternalSemaphoreHandle",
)
.expect("ReleaseExternalSemaphoreHandle")(ptr::null_mut());
}
let md = ExternalMemoryDescriptor::new(
ExternalMemoryHandleType::VkMemoryOpaqueFd,
0x1000 as *mut c_void,
4096,
);
assert_eq!(md.version, sys::API_VERSION);
assert_eq!(md.handle_type, ExternalMemoryHandleType::VkMemoryOpaqueFd);
assert_eq!(md.size_bytes, 4096);
let sd = ExternalSemaphoreDescriptor::new(
ExternalSemaphoreType::D3D12Fence,
0x2000 as *mut c_void,
);
assert_eq!(sd.version, sys::API_VERSION);
let td = ExternalTensorDescriptor::new(sys::ElementType::Float, &[2, 3]);
assert_eq!(td.version, sys::API_VERSION);
assert_eq!(td.rank, 2);
let gc = GraphicsInteropConfig::new(GraphicsApi::Vulkan, ptr::null_mut());
assert_eq!(gc.version, sys::API_VERSION);
assert_eq!(gc.graphics_api, GraphicsApi::Vulkan);
eprintln!("InteropApi gateway populated; release fns null-tolerant; descriptors sound");
}
}