1use std::sync::Arc;
2
3use crate::device::Device;
4use crate::error::{VkResult, check};
5use crate::loader::Loader;
6use crate::vk;
7use vk::handles::Handle;
8
9pub struct Instance {
42 handle: vk::handles::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::handles::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::handles::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::handles::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::handles::PhysicalDevice,
180 create_info: &vk::structs::DeviceCreateInfo,
181 allocator: Option<&vk::structs::AllocationCallbacks>,
182 ) -> VkResult<Device> {
183 let fp = self
184 .commands
185 .create_device
186 .expect("vkCreateDevice not loaded");
187 let mut raw = vk::handles::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::handles::Handle;
209
210 fn fake_handle() -> vk::handles::Instance {
211 vk::handles::Instance::from_raw(0xDEAD)
212 }
213
214 unsafe extern "system" fn mock_get_instance_proc_addr(
216 _instance: vk::handles::Instance,
217 _name: *const c_char,
218 ) -> vk::structs::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::handles::Instance,
250 name: *const c_char,
251 ) -> vk::structs::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(
257 vk::handles::Device,
258 *const c_char,
259 )
260 -> vk::structs::PFN_vkVoidFunction,
261 unsafe extern "system" fn(),
262 >(mock_device_proc_addr)
263 }),
264 b"vkCreateDevice" => Some(unsafe {
265 std::mem::transmute::<
266 unsafe extern "system" fn(
267 vk::handles::PhysicalDevice,
268 *const vk::structs::DeviceCreateInfo,
269 *const vk::structs::AllocationCallbacks,
270 *mut vk::handles::Device,
271 ) -> vk::enums::Result,
272 unsafe extern "system" fn(),
273 >(mock_create_device)
274 }),
275 _ => None,
276 }
277 }
278
279 unsafe extern "system" fn mock_device_proc_addr(
280 _device: vk::handles::Device,
281 _name: *const c_char,
282 ) -> vk::structs::PFN_vkVoidFunction {
283 None
284 }
285
286 unsafe extern "system" fn mock_create_device(
287 _physical_device: vk::handles::PhysicalDevice,
288 _p_create_info: *const vk::structs::DeviceCreateInfo,
289 _p_allocator: *const vk::structs::AllocationCallbacks,
290 p_device: *mut vk::handles::Device,
291 ) -> vk::enums::Result {
292 unsafe {
293 *p_device = std::mem::transmute::<usize, vk::handles::Device>(0xBEEF_usize);
294 }
295 vk::enums::Result::SUCCESS
296 }
297
298 fn mock_instance() -> Instance {
299 unsafe {
300 Instance::load(
301 fake_handle(),
302 Some(rich_instance_proc_addr),
303 Some(mock_device_proc_addr),
304 None,
305 )
306 }
307 }
308
309 #[test]
310 fn create_device_succeeds_with_mock() {
311 let instance = mock_instance();
312 let physical_device = vk::handles::PhysicalDevice::from_raw(0xCAFE);
313 let create_info: vk::structs::DeviceCreateInfo = unsafe { std::mem::zeroed() };
314 let device = unsafe { instance.create_device(physical_device, &create_info, None) }
315 .expect("create_device should succeed");
316 assert_eq!(device.handle().as_raw(), 0xBEEF);
317 }
318
319 #[test]
320 fn create_device_with_allocator() {
321 let instance = mock_instance();
322 let physical_device = vk::handles::PhysicalDevice::from_raw(0xCAFE);
323 let create_info: vk::structs::DeviceCreateInfo = unsafe { std::mem::zeroed() };
324 let allocator: vk::structs::AllocationCallbacks = unsafe { std::mem::zeroed() };
325 let device =
326 unsafe { instance.create_device(physical_device, &create_info, Some(&allocator)) }
327 .expect("create_device should succeed");
328 assert_eq!(device.handle().as_raw(), 0xBEEF);
329 }
330
331 #[test]
332 fn from_raw_parts_resolves_device_proc_addr() {
333 let instance =
336 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr)) };
337 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
338 let physical_device = vk::handles::PhysicalDevice::from_raw(0xCAFE);
340 let create_info: vk::structs::DeviceCreateInfo = unsafe { std::mem::zeroed() };
341 let device = unsafe { instance.create_device(physical_device, &create_info, None) }
342 .expect("create_device should succeed via from_raw_parts path");
343 assert_eq!(device.handle().as_raw(), 0xBEEF);
344 }
345
346 #[test]
347 fn load_with_loader_reference() {
348 use std::ffi::{CStr, c_void};
349 struct DummyLoader;
350 unsafe impl Loader for DummyLoader {
351 unsafe fn load(&self, _name: &CStr) -> *const c_void {
352 std::ptr::null()
353 }
354 }
355 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
356 let instance = unsafe {
357 Instance::load(
358 fake_handle(),
359 Some(mock_get_instance_proc_addr),
360 None,
361 Some(loader.clone()),
362 )
363 };
364 assert_eq!(Arc::strong_count(&loader), 2);
366 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
367 }
368
369 unsafe extern "system" fn failing_instance_proc_addr(
372 _instance: vk::handles::Instance,
373 name: *const c_char,
374 ) -> vk::structs::PFN_vkVoidFunction {
375 let name = unsafe { std::ffi::CStr::from_ptr(name) };
376 match name.to_bytes() {
377 b"vkGetDeviceProcAddr" => Some(unsafe {
378 std::mem::transmute::<
379 unsafe extern "system" fn(
380 vk::handles::Device,
381 *const c_char,
382 )
383 -> vk::structs::PFN_vkVoidFunction,
384 unsafe extern "system" fn(),
385 >(mock_device_proc_addr)
386 }),
387 b"vkCreateDevice" => Some(unsafe {
388 std::mem::transmute::<
389 unsafe extern "system" fn(
390 vk::handles::PhysicalDevice,
391 *const vk::structs::DeviceCreateInfo,
392 *const vk::structs::AllocationCallbacks,
393 *mut vk::handles::Device,
394 ) -> vk::enums::Result,
395 unsafe extern "system" fn(),
396 >(failing_create_device)
397 }),
398 _ => None,
399 }
400 }
401
402 unsafe extern "system" fn failing_create_device(
403 _physical_device: vk::handles::PhysicalDevice,
404 _p_create_info: *const vk::structs::DeviceCreateInfo,
405 _p_allocator: *const vk::structs::AllocationCallbacks,
406 _p_device: *mut vk::handles::Device,
407 ) -> vk::enums::Result {
408 vk::enums::Result::ERROR_INITIALIZATION_FAILED
409 }
410
411 #[test]
412 fn from_raw_parts_stores_no_loader() {
413 let instance =
414 unsafe { Instance::from_raw_parts(fake_handle(), Some(mock_get_instance_proc_addr)) };
415 assert_eq!(instance.handle().as_raw(), fake_handle().as_raw());
417 }
418
419 #[test]
420 fn load_with_loader_keeps_arc_alive() {
421 use std::ffi::{CStr, c_void};
422 struct DummyLoader;
423 unsafe impl Loader for DummyLoader {
424 unsafe fn load(&self, _name: &CStr) -> *const c_void {
425 std::ptr::null()
426 }
427 }
428 let loader: Arc<dyn Loader> = Arc::new(DummyLoader);
429 let weak = Arc::downgrade(&loader);
430 let instance = unsafe {
431 Instance::load(
432 fake_handle(),
433 Some(mock_get_instance_proc_addr),
434 None,
435 Some(loader),
436 )
437 };
438 assert!(weak.upgrade().is_some(), "loader should still be alive");
439 drop(instance);
440 assert!(weak.upgrade().is_none(), "loader should be dropped");
441 }
442
443 #[test]
444 fn commands_all_none_from_null_mock() {
445 let instance =
446 unsafe { Instance::load(fake_handle(), Some(mock_get_instance_proc_addr), None, None) };
447 assert!(instance.commands().enumerate_physical_devices.is_none());
449 assert!(instance.commands().destroy_instance.is_none());
450 assert!(instance.commands().create_device.is_none());
451 }
452
453 unsafe extern "system" fn mock_enumerate_physical_devices(
457 _instance: vk::handles::Instance,
458 p_count: *mut u32,
459 p_devices: *mut vk::handles::PhysicalDevice,
460 ) -> vk::enums::Result {
461 unsafe { *p_count = 2 };
462 if !p_devices.is_null() {
463 unsafe {
464 *p_devices = vk::handles::PhysicalDevice::from_raw(0xAA);
465 *p_devices.add(1) = vk::handles::PhysicalDevice::from_raw(0xBB);
466 }
467 }
468 vk::enums::Result::SUCCESS
469 }
470
471 unsafe extern "system" fn mock_destroy_instance(
472 _instance: vk::handles::Instance,
473 _p_allocator: *const vk::structs::AllocationCallbacks,
474 ) {
475 }
476
477 unsafe extern "system" fn rich_instance_proc_addr_v2(
479 _instance: vk::handles::Instance,
480 name: *const c_char,
481 ) -> vk::structs::PFN_vkVoidFunction {
482 let name = unsafe { std::ffi::CStr::from_ptr(name) };
483 match name.to_bytes() {
484 b"vkGetDeviceProcAddr" => Some(unsafe {
485 std::mem::transmute::<
486 unsafe extern "system" fn(
487 vk::handles::Device,
488 *const c_char,
489 )
490 -> vk::structs::PFN_vkVoidFunction,
491 unsafe extern "system" fn(),
492 >(mock_device_proc_addr)
493 }),
494 b"vkCreateDevice" => Some(unsafe {
495 std::mem::transmute::<
496 unsafe extern "system" fn(
497 vk::handles::PhysicalDevice,
498 *const vk::structs::DeviceCreateInfo,
499 *const vk::structs::AllocationCallbacks,
500 *mut vk::handles::Device,
501 ) -> vk::enums::Result,
502 unsafe extern "system" fn(),
503 >(mock_create_device)
504 }),
505 b"vkEnumeratePhysicalDevices" => Some(unsafe {
506 std::mem::transmute::<
507 unsafe extern "system" fn(
508 vk::handles::Instance,
509 *mut u32,
510 *mut vk::handles::PhysicalDevice,
511 ) -> vk::enums::Result,
512 unsafe extern "system" fn(),
513 >(mock_enumerate_physical_devices)
514 }),
515 b"vkDestroyInstance" => Some(unsafe {
516 std::mem::transmute::<
517 unsafe extern "system" fn(
518 vk::handles::Instance,
519 *const vk::structs::AllocationCallbacks,
520 ),
521 unsafe extern "system" fn(),
522 >(mock_destroy_instance)
523 }),
524 _ => None,
525 }
526 }
527
528 #[test]
529 fn from_raw_parts_populates_commands_from_rich_mock() {
530 let instance =
531 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
532 assert!(instance.commands().enumerate_physical_devices.is_some());
533 assert!(instance.commands().destroy_instance.is_some());
534 assert!(instance.commands().create_device.is_some());
535 assert!(instance.commands().get_physical_device_properties.is_none());
537 }
538
539 #[test]
540 fn enumerate_physical_devices_with_mock() {
541 let instance =
542 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
543 let devices =
544 unsafe { instance.enumerate_physical_devices() }.expect("enumerate should succeed");
545 assert_eq!(devices.len(), 2);
546 assert_eq!(devices[0].as_raw(), 0xAA);
547 assert_eq!(devices[1].as_raw(), 0xBB);
548 }
549
550 #[test]
551 fn destroy_instance_with_mock() {
552 let instance =
553 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
554 unsafe { instance.destroy_instance(None) };
556 }
557
558 #[test]
559 fn create_device_from_raw_parts_instance() {
560 let instance =
563 unsafe { Instance::from_raw_parts(fake_handle(), Some(rich_instance_proc_addr_v2)) };
564 let create_info: vk::structs::DeviceCreateInfo = unsafe { std::mem::zeroed() };
565 let device = unsafe {
566 instance.create_device(
567 vk::handles::PhysicalDevice::from_raw(0xAA),
568 &create_info,
569 None,
570 )
571 }
572 .expect("create_device should succeed");
573 assert_eq!(device.handle().as_raw(), 0xBEEF);
574 }
575
576 #[test]
577 fn create_device_propagates_error() {
578 let instance = unsafe {
579 Instance::load(
580 fake_handle(),
581 Some(failing_instance_proc_addr),
582 Some(mock_device_proc_addr),
583 None,
584 )
585 };
586 let physical_device = vk::handles::PhysicalDevice::from_raw(0xCAFE);
587 let create_info: vk::structs::DeviceCreateInfo = unsafe { std::mem::zeroed() };
588 let result = unsafe { instance.create_device(physical_device, &create_info, None) };
589 match result {
590 Err(e) => assert_eq!(e, vk::enums::Result::ERROR_INITIALIZATION_FAILED),
591 Ok(_) => panic!("expected error, got Ok"),
592 }
593 }
594
595 #[test]
596 #[ignore] fn enumerate_physical_devices_returns_at_least_one() {
598 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
599 let instance = create_real_instance();
600 let devices = unsafe { instance.enumerate_physical_devices() }
601 .expect("enumerate_physical_devices failed");
602 assert!(!devices.is_empty(), "expected at least one physical device");
603 }
604
605 #[test]
606 #[ignore] fn get_physical_device_properties_succeeds() {
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 props = unsafe { instance.get_physical_device_properties(devices[0]) };
613 let name_bytes: Vec<u8> = props
614 .device_name
615 .iter()
616 .take_while(|&&c| c != 0)
617 .map(|&c| c as u8)
618 .collect();
619 let name = String::from_utf8_lossy(&name_bytes);
620 println!("GPU: {name}");
621 assert!(!name.is_empty());
622 }
623
624 #[test]
625 #[ignore] fn get_physical_device_queue_family_properties_returns_at_least_one() {
627 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
628 let instance = create_real_instance();
629 let devices = unsafe { instance.enumerate_physical_devices() }
630 .expect("enumerate_physical_devices failed");
631 let families = unsafe { instance.get_physical_device_queue_family_properties(devices[0]) };
632 assert!(!families.is_empty(), "expected at least one queue family");
633 }
634
635 #[test]
636 #[ignore] fn get_physical_device_memory_properties_succeeds() {
638 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
639 let instance = create_real_instance();
640 let devices = unsafe { instance.enumerate_physical_devices() }
641 .expect("enumerate_physical_devices failed");
642 let mem_props = unsafe { instance.get_physical_device_memory_properties(devices[0]) };
643 assert!(
644 mem_props.memory_type_count > 0,
645 "expected at least one memory type"
646 );
647 assert!(
648 mem_props.memory_heap_count > 0,
649 "expected at least one memory heap"
650 );
651 }
652
653 #[test]
654 #[ignore] fn get_physical_device_features_succeeds() {
656 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
657 let instance = create_real_instance();
658 let devices = unsafe { instance.enumerate_physical_devices() }
659 .expect("enumerate_physical_devices failed");
660 let _features = unsafe { instance.get_physical_device_features(devices[0]) };
663 }
664
665 fn create_real_instance() -> Instance {
666 use crate::entry::Entry;
667 use crate::loader::LibloadingLoader;
668
669 let loader = LibloadingLoader::new().expect("failed to load Vulkan");
670 let entry = unsafe { Entry::new(loader) }.expect("failed to create Entry");
671
672 let api_version_1_0 = 1u32 << 22;
674
675 let app_info = vk::structs::ApplicationInfo {
676 s_type: vk::enums::StructureType::APPLICATION_INFO,
677 p_next: std::ptr::null(),
678 p_application_name: std::ptr::null(),
679 application_version: 0,
680 p_engine_name: std::ptr::null(),
681 engine_version: 0,
682 api_version: api_version_1_0,
683 };
684 let create_info = vk::structs::InstanceCreateInfo {
685 s_type: vk::enums::StructureType::INSTANCE_CREATE_INFO,
686 p_next: std::ptr::null(),
687 flags: vk::bitmasks::InstanceCreateFlagBits::empty(),
688 p_application_info: &app_info,
689 enabled_layer_count: 0,
690 pp_enabled_layer_names: std::ptr::null(),
691 enabled_extension_count: 0,
692 pp_enabled_extension_names: std::ptr::null(),
693 };
694
695 unsafe { entry.create_instance(&create_info, None) }.expect("failed to create instance")
696 }
697}