1use std::sync::Arc;
2
3use crate::device::Device;
4use crate::error::{VkResult, check};
5use crate::loader::Loader;
6use crate::vk;
7use vk::Handle;
8
9pub struct Instance {
42 handle: vk::Instance,
43 commands: Box<vk::commands::InstanceCommands>,
44 get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr,
45 _loader: Option<Arc<dyn Loader>>,
46}
47
48impl Instance {
49 pub(crate) unsafe fn load(
63 handle: vk::Instance,
64 get_instance_proc_addr: vk::commands::PFN_vkGetInstanceProcAddr,
65 get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr,
66 loader: Option<Arc<dyn Loader>>,
67 ) -> Self {
68 let get_instance_proc_addr_fn =
69 get_instance_proc_addr.expect("vkGetInstanceProcAddr not loaded");
70 let commands = Box::new(unsafe {
72 vk::commands::InstanceCommands::load(|name| {
73 std::mem::transmute(get_instance_proc_addr_fn(handle, name.as_ptr()))
74 })
75 });
76 Self {
77 handle,
78 commands,
79 get_device_proc_addr,
80 _loader: loader,
81 }
82 }
83
84 pub unsafe fn from_raw_parts(
115 handle: vk::Instance,
116 get_instance_proc_addr: vk::commands::PFN_vkGetInstanceProcAddr,
117 ) -> Self {
118 let get_instance_proc_addr_fn =
119 get_instance_proc_addr.expect("vkGetInstanceProcAddr not loaded");
120
121 let get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr = unsafe {
123 std::mem::transmute(get_instance_proc_addr_fn(
124 handle,
125 c"vkGetDeviceProcAddr".as_ptr(),
126 ))
127 };
128
129 unsafe { Self::load(handle, get_instance_proc_addr, get_device_proc_addr, None) }
131 }
132
133 pub fn handle(&self) -> vk::Instance {
135 self.handle
136 }
137
138 pub fn commands(&self) -> &vk::commands::InstanceCommands {
143 &self.commands
144 }
145
146 pub unsafe fn create_device(
178 &self,
179 physical_device: vk::PhysicalDevice,
180 create_info: &vk::DeviceCreateInfo,
181 allocator: Option<&vk::AllocationCallbacks>,
182 ) -> VkResult<Device> {
183 let fp = self
184 .commands
185 .create_device
186 .expect("vkCreateDevice not loaded");
187 let mut raw = vk::Device::null();
188 let result = unsafe {
190 fp(
191 physical_device,
192 create_info,
193 allocator.map_or(std::ptr::null(), |a| a),
194 &mut raw,
195 )
196 };
197 check(result)?;
198 let device = unsafe { Device::load(raw, self.get_device_proc_addr, self._loader.clone()) };
200 Ok(device)
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use std::ffi::c_char;
208 use vk::Handle;
209
210 fn fake_handle() -> vk::Instance {
211 vk::Instance::from_raw(0xDEAD)
212 }
213
214 unsafe extern "system" fn mock_get_instance_proc_addr(
216 _instance: vk::Instance,
217 _name: *const c_char,
218 ) -> vk::PFN_vkVoidFunction {
219 None
220 }
221
222 #[test]
223 fn from_raw_parts_stores_handle() {
224 let instance =
225 unsafe { Instance::from_raw_parts(fake_handle(), Some(mock_get_instance_proc_addr)) };
226 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
227 }
228
229 #[test]
230 fn handle_returns_value_from_construction() {
231 let instance =
232 unsafe { Instance::load(fake_handle(), Some(mock_get_instance_proc_addr), None, None) };
233 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
234 }
235
236 #[test]
237 fn commands_returns_reference() {
238 let instance =
239 unsafe { Instance::load(fake_handle(), Some(mock_get_instance_proc_addr), None, None) };
240 let _ = instance.commands();
243 }
244
245 unsafe extern "system" fn rich_instance_proc_addr(
249 _instance: vk::Instance,
250 name: *const c_char,
251 ) -> vk::PFN_vkVoidFunction {
252 let name = unsafe { std::ffi::CStr::from_ptr(name) };
253 match name.to_bytes() {
254 b"vkGetDeviceProcAddr" => Some(unsafe {
255 std::mem::transmute::<
256 unsafe extern "system" fn(vk::Device, *const c_char) -> vk::PFN_vkVoidFunction,
257 unsafe extern "system" fn(),
258 >(mock_device_proc_addr)
259 }),
260 b"vkCreateDevice" => Some(unsafe {
261 std::mem::transmute::<
262 unsafe extern "system" fn(
263 vk::PhysicalDevice,
264 *const vk::DeviceCreateInfo,
265 *const vk::AllocationCallbacks,
266 *mut vk::Device,
267 ) -> vk::Result,
268 unsafe extern "system" fn(),
269 >(mock_create_device)
270 }),
271 _ => None,
272 }
273 }
274
275 unsafe extern "system" fn mock_device_proc_addr(
276 _device: vk::Device,
277 _name: *const c_char,
278 ) -> vk::PFN_vkVoidFunction {
279 None
280 }
281
282 unsafe extern "system" fn mock_create_device(
283 _physical_device: vk::PhysicalDevice,
284 _p_create_info: *const vk::DeviceCreateInfo,
285 _p_allocator: *const vk::AllocationCallbacks,
286 p_device: *mut vk::Device,
287 ) -> vk::Result {
288 unsafe {
289 *p_device = std::mem::transmute::<usize, vk::Device>(0xBEEF_usize);
290 }
291 vk::Result::SUCCESS
292 }
293
294 fn mock_instance() -> Instance {
295 unsafe {
296 Instance::load(
297 fake_handle(),
298 Some(rich_instance_proc_addr),
299 Some(mock_device_proc_addr),
300 None,
301 )
302 }
303 }
304
305 #[test]
306 fn create_device_succeeds_with_mock() {
307 let instance = mock_instance();
308 let physical_device = vk::PhysicalDevice::from_raw(0xCAFE);
309 let create_info: vk::DeviceCreateInfo = unsafe { std::mem::zeroed() };
310 let device = unsafe { instance.create_device(physical_device, &create_info, None) }
311 .expect("create_device should succeed");
312 assert_eq!(device.handle().as_raw(), 0xBEEF);
313 }
314
315 #[test]
316 fn create_device_with_allocator() {
317 let instance = mock_instance();
318 let physical_device = vk::PhysicalDevice::from_raw(0xCAFE);
319 let create_info: vk::DeviceCreateInfo = unsafe { std::mem::zeroed() };
320 let allocator: vk::AllocationCallbacks = unsafe { std::mem::zeroed() };
321 let device =
322 unsafe { instance.create_device(physical_device, &create_info, Some(&allocator)) }
323 .expect("create_device should succeed");
324 assert_eq!(device.handle().as_raw(), 0xBEEF);
325 }
326
327 #[test]
328 fn from_raw_parts_resolves_device_proc_addr() {
329 let instance =
332 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr)) };
333 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
334 let physical_device = vk::PhysicalDevice::from_raw(0xCAFE);
336 let create_info: vk::DeviceCreateInfo = unsafe { std::mem::zeroed() };
337 let device = unsafe { instance.create_device(physical_device, &create_info, None) }
338 .expect("create_device should succeed via from_raw_parts path");
339 assert_eq!(device.handle().as_raw(), 0xBEEF);
340 }
341
342 #[test]
343 fn load_with_loader_reference() {
344 use std::ffi::{CStr, c_void};
345 struct DummyLoader;
346 unsafe impl Loader for DummyLoader {
347 unsafe fn load(&self, _name: &CStr) -> *const c_void {
348 std::ptr::null()
349 }
350 }
351 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
352 let instance = unsafe {
353 Instance::load(
354 fake_handle(),
355 Some(mock_get_instance_proc_addr),
356 None,
357 Some(loader.clone()),
358 )
359 };
360 assert_eq!(Arc::strong_count(&loader), 2);
362 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
363 }
364
365 unsafe extern "system" fn failing_instance_proc_addr(
368 _instance: vk::Instance,
369 name: *const c_char,
370 ) -> vk::PFN_vkVoidFunction {
371 let name = unsafe { std::ffi::CStr::from_ptr(name) };
372 match name.to_bytes() {
373 b"vkGetDeviceProcAddr" => Some(unsafe {
374 std::mem::transmute::<
375 unsafe extern "system" fn(vk::Device, *const c_char) -> vk::PFN_vkVoidFunction,
376 unsafe extern "system" fn(),
377 >(mock_device_proc_addr)
378 }),
379 b"vkCreateDevice" => Some(unsafe {
380 std::mem::transmute::<
381 unsafe extern "system" fn(
382 vk::PhysicalDevice,
383 *const vk::DeviceCreateInfo,
384 *const vk::AllocationCallbacks,
385 *mut vk::Device,
386 ) -> vk::Result,
387 unsafe extern "system" fn(),
388 >(failing_create_device)
389 }),
390 _ => None,
391 }
392 }
393
394 unsafe extern "system" fn failing_create_device(
395 _physical_device: vk::PhysicalDevice,
396 _p_create_info: *const vk::DeviceCreateInfo,
397 _p_allocator: *const vk::AllocationCallbacks,
398 _p_device: *mut vk::Device,
399 ) -> vk::Result {
400 vk::Result::ERROR_INITIALIZATION_FAILED
401 }
402
403 #[test]
404 fn from_raw_parts_stores_no_loader() {
405 let instance =
406 unsafe { Instance::from_raw_parts(fake_handle(), Some(mock_get_instance_proc_addr)) };
407 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
409 }
410
411 #[test]
412 fn load_with_loader_keeps_arc_alive() {
413 use std::ffi::{CStr, c_void};
414 struct DummyLoader;
415 unsafe impl Loader for DummyLoader {
416 unsafe fn load(&self, _name: &CStr) -> *const c_void {
417 std::ptr::null()
418 }
419 }
420 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
421 let weak = Arc::downgrade(&loader);
422 let instance = unsafe {
423 Instance::load(
424 fake_handle(),
425 Some(mock_get_instance_proc_addr),
426 None,
427 Some(loader),
428 )
429 };
430 assert!(weak.upgrade().is_some(), "loader should still be alive");
431 drop(instance);
432 assert!(weak.upgrade().is_none(), "loader should be dropped");
433 }
434
435 #[test]
436 fn commands_all_none_from_null_mock() {
437 let instance =
438 unsafe { Instance::load(fake_handle(), Some(mock_get_instance_proc_addr), None, None) };
439 assert!(instance.commands().enumerate_physical_devices.is_none());
441 assert!(instance.commands().destroy_instance.is_none());
442 assert!(instance.commands().create_device.is_none());
443 }
444
445 unsafe extern "system" fn mock_enumerate_physical_devices(
449 _instance: vk::Instance,
450 p_count: *mut u32,
451 p_devices: *mut vk::PhysicalDevice,
452 ) -> vk::Result {
453 unsafe { *p_count = 2 };
454 if !p_devices.is_null() {
455 unsafe {
456 *p_devices = vk::PhysicalDevice::from_raw(0xAA);
457 *p_devices.add(1) = vk::PhysicalDevice::from_raw(0xBB);
458 }
459 }
460 vk::Result::SUCCESS
461 }
462
463 unsafe extern "system" fn mock_destroy_instance(
464 _instance: vk::Instance,
465 _p_allocator: *const vk::AllocationCallbacks,
466 ) {
467 }
468
469 unsafe extern "system" fn rich_instance_proc_addr_v2(
471 _instance: vk::Instance,
472 name: *const c_char,
473 ) -> vk::PFN_vkVoidFunction {
474 let name = unsafe { std::ffi::CStr::from_ptr(name) };
475 match name.to_bytes() {
476 b"vkGetDeviceProcAddr" => Some(unsafe {
477 std::mem::transmute::<
478 unsafe extern "system" fn(vk::Device, *const c_char) -> vk::PFN_vkVoidFunction,
479 unsafe extern "system" fn(),
480 >(mock_device_proc_addr)
481 }),
482 b"vkCreateDevice" => Some(unsafe {
483 std::mem::transmute::<
484 unsafe extern "system" fn(
485 vk::PhysicalDevice,
486 *const vk::DeviceCreateInfo,
487 *const vk::AllocationCallbacks,
488 *mut vk::Device,
489 ) -> vk::Result,
490 unsafe extern "system" fn(),
491 >(mock_create_device)
492 }),
493 b"vkEnumeratePhysicalDevices" => Some(unsafe {
494 std::mem::transmute::<
495 unsafe extern "system" fn(
496 vk::Instance,
497 *mut u32,
498 *mut vk::PhysicalDevice,
499 ) -> vk::Result,
500 unsafe extern "system" fn(),
501 >(mock_enumerate_physical_devices)
502 }),
503 b"vkDestroyInstance" => Some(unsafe {
504 std::mem::transmute::<
505 unsafe extern "system" fn(vk::Instance, *const vk::AllocationCallbacks),
506 unsafe extern "system" fn(),
507 >(mock_destroy_instance)
508 }),
509 _ => None,
510 }
511 }
512
513 #[test]
514 fn from_raw_parts_populates_commands_from_rich_mock() {
515 let instance =
516 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
517 assert!(instance.commands().enumerate_physical_devices.is_some());
518 assert!(instance.commands().destroy_instance.is_some());
519 assert!(instance.commands().create_device.is_some());
520 assert!(instance.commands().get_physical_device_properties.is_none());
522 }
523
524 #[test]
525 fn enumerate_physical_devices_with_mock() {
526 let instance =
527 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
528 let devices =
529 unsafe { instance.enumerate_physical_devices() }.expect("enumerate should succeed");
530 assert_eq!(devices.len(), 2);
531 assert_eq!(devices[0].as_raw(), 0xAA);
532 assert_eq!(devices[1].as_raw(), 0xBB);
533 }
534
535 #[test]
536 fn destroy_instance_with_mock() {
537 let instance =
538 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
539 unsafe { instance.destroy_instance(None) };
541 }
542
543 #[test]
544 fn create_device_from_raw_parts_instance() {
545 let instance =
548 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
549 let create_info: vk::DeviceCreateInfo = unsafe { std::mem::zeroed() };
550 let device = unsafe {
551 instance.create_device(vk::PhysicalDevice::from_raw(0xAA), &create_info, None)
552 }
553 .expect("create_device should succeed");
554 assert_eq!(device.handle().as_raw(), 0xBEEF);
555 }
556
557 #[test]
558 fn create_device_propagates_error() {
559 let instance = unsafe {
560 Instance::load(
561 fake_handle(),
562 Some(failing_instance_proc_addr),
563 Some(mock_device_proc_addr),
564 None,
565 )
566 };
567 let physical_device = vk::PhysicalDevice::from_raw(0xCAFE);
568 let create_info: vk::DeviceCreateInfo = unsafe { std::mem::zeroed() };
569 let result = unsafe { instance.create_device(physical_device, &create_info, None) };
570 match result {
571 Err(e) => assert_eq!(e, vk::Result::ERROR_INITIALIZATION_FAILED),
572 Ok(_) => panic!("expected error, got Ok"),
573 }
574 }
575
576 #[test]
577 #[ignore] fn enumerate_physical_devices_returns_at_least_one() {
579 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
580 let instance = create_real_instance();
581 let devices = unsafe { instance.enumerate_physical_devices() }
582 .expect("enumerate_physical_devices failed");
583 assert!(!devices.is_empty(), "expected at least one physical device");
584 }
585
586 #[test]
587 #[ignore] fn get_physical_device_properties_succeeds() {
589 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
590 let instance = create_real_instance();
591 let devices = unsafe { instance.enumerate_physical_devices() }
592 .expect("enumerate_physical_devices failed");
593 let props = unsafe { instance.get_physical_device_properties(devices[0]) };
594 let name_bytes: Vec<u8> = props
595 .device_name
596 .iter()
597 .take_while(|&&c| c != 0)
598 .map(|&c| c as u8)
599 .collect();
600 let name = String::from_utf8_lossy(&name_bytes);
601 println!("GPU: {name}");
602 assert!(!name.is_empty());
603 }
604
605 #[test]
606 #[ignore] fn get_physical_device_queue_family_properties_returns_at_least_one() {
608 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
609 let instance = create_real_instance();
610 let devices = unsafe { instance.enumerate_physical_devices() }
611 .expect("enumerate_physical_devices failed");
612 let families = unsafe { instance.get_physical_device_queue_family_properties(devices[0]) };
613 assert!(!families.is_empty(), "expected at least one queue family");
614 }
615
616 #[test]
617 #[ignore] fn get_physical_device_memory_properties_succeeds() {
619 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
620 let instance = create_real_instance();
621 let devices = unsafe { instance.enumerate_physical_devices() }
622 .expect("enumerate_physical_devices failed");
623 let mem_props = unsafe { instance.get_physical_device_memory_properties(devices[0]) };
624 assert!(
625 mem_props.memory_type_count > 0,
626 "expected at least one memory type"
627 );
628 assert!(
629 mem_props.memory_heap_count > 0,
630 "expected at least one memory heap"
631 );
632 }
633
634 #[test]
635 #[ignore] fn get_physical_device_features_succeeds() {
637 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
638 let instance = create_real_instance();
639 let devices = unsafe { instance.enumerate_physical_devices() }
640 .expect("enumerate_physical_devices failed");
641 let _features = unsafe { instance.get_physical_device_features(devices[0]) };
644 }
645
646 fn create_real_instance() -> Instance {
647 use crate::entry::Entry;
648 use crate::loader::LibloadingLoader;
649
650 let loader = LibloadingLoader::new().expect("failed to load Vulkan");
651 let entry = unsafe { Entry::new(loader) }.expect("failed to create Entry");
652
653 let api_version_1_0 = crate::Version::new(1, 0, 0).to_raw();
654
655 let app_info = vk::ApplicationInfo {
656 s_type: vk::StructureType::APPLICATION_INFO,
657 p_next: std::ptr::null(),
658 p_application_name: std::ptr::null(),
659 application_version: 0,
660 p_engine_name: std::ptr::null(),
661 engine_version: 0,
662 api_version: api_version_1_0,
663 };
664 let create_info = vk::InstanceCreateInfo {
665 s_type: vk::StructureType::INSTANCE_CREATE_INFO,
666 p_next: std::ptr::null(),
667 flags: vk::InstanceCreateFlagBits::empty(),
668 p_application_info: &app_info,
669 enabled_layer_count: 0,
670 pp_enabled_layer_names: std::ptr::null(),
671 enabled_extension_count: 0,
672 pp_enabled_extension_names: std::ptr::null(),
673 };
674
675 unsafe { entry.create_instance(&create_info, None) }.expect("failed to create instance")
676 }
677}