1use std::ffi::{c_char, c_void};
20use std::os::fd::{FromRawFd, OwnedFd};
21use std::ptr;
22use std::sync::OnceLock;
23
24use libloading::Library;
25
26type VkResult = i32;
27type VkInstance = *mut c_void;
28type VkPhysicalDevice = *mut c_void;
29type VkDevice = *mut c_void;
30type VkSemaphore = u64;
31type VoidFn = Option<unsafe extern "C" fn()>;
32
33const VK_API_VERSION_1_1: u32 = (1 << 22) | (1 << 12);
34const STRUCTURE_TYPE_APPLICATION_INFO: i32 = 0;
35const STRUCTURE_TYPE_INSTANCE_CREATE_INFO: i32 = 1;
36const STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO: i32 = 2;
37const STRUCTURE_TYPE_DEVICE_CREATE_INFO: i32 = 3;
38const STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO: i32 = 9;
39const STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2: i32 = 1000059001;
40const STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: i32 = 1000071004;
41const STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO: i32 = 1000077000;
42const STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR: i32 = 1000079001;
43const EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD: u32 = 0x1;
44
45#[repr(C)]
46struct ApplicationInfo {
47 s_type: i32,
48 p_next: *const c_void,
49 application_name: *const c_char,
50 application_version: u32,
51 engine_name: *const c_char,
52 engine_version: u32,
53 api_version: u32,
54}
55
56#[repr(C)]
57struct InstanceCreateInfo {
58 s_type: i32,
59 p_next: *const c_void,
60 flags: u32,
61 application_info: *const ApplicationInfo,
62 enabled_layer_count: u32,
63 enabled_layer_names: *const *const c_char,
64 enabled_extension_count: u32,
65 enabled_extension_names: *const *const c_char,
66}
67
68#[repr(C)]
69struct DeviceQueueCreateInfo {
70 s_type: i32,
71 p_next: *const c_void,
72 flags: u32,
73 queue_family_index: u32,
74 queue_count: u32,
75 queue_priorities: *const f32,
76}
77
78#[repr(C)]
79struct DeviceCreateInfo {
80 s_type: i32,
81 p_next: *const c_void,
82 flags: u32,
83 queue_create_info_count: u32,
84 queue_create_infos: *const DeviceQueueCreateInfo,
85 enabled_layer_count: u32,
86 enabled_layer_names: *const *const c_char,
87 enabled_extension_count: u32,
88 enabled_extension_names: *const *const c_char,
89 enabled_features: *const c_void,
90}
91
92#[repr(C)]
93struct PhysicalDeviceIdProperties {
94 s_type: i32,
95 p_next: *mut c_void,
96 device_uuid: [u8; 16],
97 driver_uuid: [u8; 16],
98 device_luid: [u8; 8],
99 device_node_mask: u32,
100 device_luid_valid: u32,
101}
102
103#[repr(C)]
106struct PhysicalDeviceProperties2 {
107 s_type: i32,
108 p_next: *mut c_void,
109 properties: [u64; 103],
110}
111
112#[repr(C)]
113struct ExportSemaphoreCreateInfo {
114 s_type: i32,
115 p_next: *const c_void,
116 handle_types: u32,
117}
118
119#[repr(C)]
120struct SemaphoreCreateInfo {
121 s_type: i32,
122 p_next: *const c_void,
123 flags: u32,
124}
125
126#[repr(C)]
127struct SemaphoreGetFdInfo {
128 s_type: i32,
129 p_next: *const c_void,
130 semaphore: VkSemaphore,
131 handle_type: u32,
132}
133
134type GetInstanceProcAddr = unsafe extern "C" fn(VkInstance, *const c_char) -> VoidFn;
135type CreateInstance =
136 unsafe extern "C" fn(*const InstanceCreateInfo, *const c_void, *mut VkInstance) -> VkResult;
137type DestroyInstance = unsafe extern "C" fn(VkInstance, *const c_void);
138type EnumeratePhysicalDevices =
139 unsafe extern "C" fn(VkInstance, *mut u32, *mut VkPhysicalDevice) -> VkResult;
140type GetPhysicalDeviceProperties2 =
141 unsafe extern "C" fn(VkPhysicalDevice, *mut PhysicalDeviceProperties2);
142type CreateDevice = unsafe extern "C" fn(
143 VkPhysicalDevice,
144 *const DeviceCreateInfo,
145 *const c_void,
146 *mut VkDevice,
147) -> VkResult;
148type GetDeviceProcAddr = unsafe extern "C" fn(VkDevice, *const c_char) -> VoidFn;
149type DestroyDevice = unsafe extern "C" fn(VkDevice, *const c_void);
150type CreateSemaphore = unsafe extern "C" fn(
151 VkDevice,
152 *const SemaphoreCreateInfo,
153 *const c_void,
154 *mut VkSemaphore,
155) -> VkResult;
156type DestroySemaphore = unsafe extern "C" fn(VkDevice, VkSemaphore, *const c_void);
157type GetSemaphoreFd =
158 unsafe extern "C" fn(VkDevice, *const SemaphoreGetFdInfo, *mut i32) -> VkResult;
159
160struct Vulkan {
166 _lib: Library,
167 instance: VkInstance,
168 enumerate: EnumeratePhysicalDevices,
169 properties: GetPhysicalDeviceProperties2,
170 create_device: CreateDevice,
171 get_device_proc: GetDeviceProcAddr,
172}
173
174unsafe impl Send for Vulkan {}
178unsafe impl Sync for Vulkan {}
179
180impl Vulkan {
181 fn get() -> Result<&'static Vulkan, String> {
184 static VULKAN: OnceLock<Result<Vulkan, String>> = OnceLock::new();
185 VULKAN
186 .get_or_init(|| unsafe { Self::open() })
187 .as_ref()
188 .map_err(Clone::clone)
189 }
190
191 unsafe fn open() -> Result<Self, String> {
198 unsafe {
199 let lib = Library::new("libvulkan.so.1")
200 .map_err(|e| format!("no Vulkan loader (libvulkan.so.1): {e}"))?;
201 let get_instance_proc: GetInstanceProcAddr = *lib
202 .get(b"vkGetInstanceProcAddr\0")
203 .map_err(|e| format!("no vkGetInstanceProcAddr: {e}"))?;
204 macro_rules! instance_fn {
205 ($instance:expr, $ty:ty, $name:literal) => {
206 match get_instance_proc($instance, $name.as_ptr()) {
207 Some(f) => Ok(std::mem::transmute::<unsafe extern "C" fn(), $ty>(f)),
208 None => Err(format!("no {}", $name.to_string_lossy())),
209 }
210 };
211 }
212 let create_instance: CreateInstance =
213 instance_fn!(ptr::null_mut(), CreateInstance, c"vkCreateInstance")?;
214 let app = ApplicationInfo {
215 s_type: STRUCTURE_TYPE_APPLICATION_INFO,
216 p_next: ptr::null(),
217 application_name: c"pixelflux".as_ptr(),
218 application_version: 1,
219 engine_name: ptr::null(),
220 engine_version: 0,
221 api_version: VK_API_VERSION_1_1,
222 };
223 let instance_info = InstanceCreateInfo {
224 s_type: STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
225 p_next: ptr::null(),
226 flags: 0,
227 application_info: &app,
228 enabled_layer_count: 0,
229 enabled_layer_names: ptr::null(),
230 enabled_extension_count: 0,
231 enabled_extension_names: ptr::null(),
232 };
233 let mut instance: VkInstance = ptr::null_mut();
234 let r = create_instance(&instance_info, ptr::null(), &mut instance);
235 if r != 0 {
236 return Err(format!("vkCreateInstance: {r}"));
237 }
238 let destroy_instance: DestroyInstance =
239 instance_fn!(instance, DestroyInstance, c"vkDestroyInstance")?;
240 let entry_points = (|| {
241 Ok((
242 instance_fn!(
243 instance,
244 EnumeratePhysicalDevices,
245 c"vkEnumeratePhysicalDevices"
246 )?,
247 instance_fn!(
248 instance,
249 GetPhysicalDeviceProperties2,
250 c"vkGetPhysicalDeviceProperties2"
251 )?,
252 instance_fn!(instance, CreateDevice, c"vkCreateDevice")?,
253 instance_fn!(instance, GetDeviceProcAddr, c"vkGetDeviceProcAddr")?,
254 ))
255 })();
256 match entry_points {
257 Ok((enumerate, properties, create_device, get_device_proc)) => Ok(Self {
258 _lib: lib,
259 instance,
260 enumerate,
261 properties,
262 create_device,
263 get_device_proc,
264 }),
265 Err(e) => {
266 destroy_instance(instance, ptr::null());
267 Err(e)
268 }
269 }
270 }
271 }
272}
273
274pub(crate) fn opaque_fd_pair(uuid: [u8; 16]) -> Result<(OwnedFd, OwnedFd), String> {
277 let vk = Vulkan::get()?;
278 let instance = vk.instance;
279 unsafe {
280 let mut count = 0u32;
281 (vk.enumerate)(instance, &mut count, ptr::null_mut());
282 let mut devices: Vec<VkPhysicalDevice> = Vec::with_capacity(count as usize);
283 (vk.enumerate)(instance, &mut count, devices.as_mut_ptr());
284 devices.set_len(count as usize);
285 let physical = devices
286 .into_iter()
287 .find(|&d| {
288 let mut id = PhysicalDeviceIdProperties {
289 s_type: STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES,
290 p_next: ptr::null_mut(),
291 device_uuid: [0; 16],
292 driver_uuid: [0; 16],
293 device_luid: [0; 8],
294 device_node_mask: 0,
295 device_luid_valid: 0,
296 };
297 let mut props = PhysicalDeviceProperties2 {
298 s_type: STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
299 p_next: (&mut id as *mut PhysicalDeviceIdProperties).cast(),
300 properties: [0; 103],
301 };
302 (vk.properties)(d, &mut props);
303 id.device_uuid == uuid
304 })
305 .ok_or("no Vulkan device has the CUDA device's UUID")?;
306
307 let priority = 1.0f32;
308 let queue = DeviceQueueCreateInfo {
309 s_type: STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
310 p_next: ptr::null(),
311 flags: 0,
312 queue_family_index: 0,
313 queue_count: 1,
314 queue_priorities: &priority,
315 };
316 let extensions = [c"VK_KHR_external_semaphore_fd".as_ptr()];
317 let device_info = DeviceCreateInfo {
318 s_type: STRUCTURE_TYPE_DEVICE_CREATE_INFO,
319 p_next: ptr::null(),
320 flags: 0,
321 queue_create_info_count: 1,
322 queue_create_infos: &queue,
323 enabled_layer_count: 0,
324 enabled_layer_names: ptr::null(),
325 enabled_extension_count: 1,
326 enabled_extension_names: extensions.as_ptr(),
327 enabled_features: ptr::null(),
328 };
329 let mut device: VkDevice = ptr::null_mut();
330 let r = (vk.create_device)(physical, &device_info, ptr::null(), &mut device);
331 if r != 0 {
332 return Err(format!("vkCreateDevice: {r}"));
333 }
334 macro_rules! device_fn {
335 ($ty:ty, $name:literal) => {
336 match (vk.get_device_proc)(device, $name.as_ptr()) {
337 Some(f) => std::mem::transmute::<unsafe extern "C" fn(), $ty>(f),
338 None => return Err(format!("no {}", $name.to_string_lossy())),
339 }
340 };
341 }
342 let destroy_device: DestroyDevice = device_fn!(DestroyDevice, c"vkDestroyDevice");
343 let fds = (|| {
344 let create_semaphore = device_fn!(CreateSemaphore, c"vkCreateSemaphore");
345 let destroy_semaphore = device_fn!(DestroySemaphore, c"vkDestroySemaphore");
346 let get_fd = device_fn!(GetSemaphoreFd, c"vkGetSemaphoreFdKHR");
347 let export = ExportSemaphoreCreateInfo {
348 s_type: STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
349 p_next: ptr::null(),
350 handle_types: EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD,
351 };
352 let info = SemaphoreCreateInfo {
353 s_type: STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
354 p_next: (&export as *const ExportSemaphoreCreateInfo).cast(),
355 flags: 0,
356 };
357 let mut semaphore: VkSemaphore = 0;
358 let r = create_semaphore(device, &info, ptr::null(), &mut semaphore);
359 if r != 0 {
360 return Err(format!("vkCreateSemaphore: {r}"));
361 }
362 let fd_info = SemaphoreGetFdInfo {
363 s_type: STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
364 p_next: ptr::null(),
365 semaphore,
366 handle_type: EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD,
367 };
368 let export_one = || -> Result<OwnedFd, String> {
369 let mut fd = -1;
370 let r = get_fd(device, &fd_info, &mut fd);
371 if r != 0 || fd < 0 {
372 return Err(format!("vkGetSemaphoreFdKHR: {r}"));
373 }
374 Ok(OwnedFd::from_raw_fd(fd))
375 };
376 let pair = export_one().and_then(|a| Ok((a, export_one()?)));
377 destroy_semaphore(device, semaphore, ptr::null());
378 pair
379 })();
380 destroy_device(device, ptr::null());
381 fds
382 }
383}