1use std::sync::Arc;
2
3use std::ffi::CStr;
4
5use crate::error::{LoadError, VkResult, check, enumerate_two_call};
6use crate::instance::Instance;
7use crate::loader::Loader;
8use crate::version::Version;
9use crate::vk;
10use vk::handles::Handle;
11
12pub struct Entry {
36 _loader: Arc<dyn Loader>,
37 get_instance_proc_addr: vk::commands::PFN_vkGetInstanceProcAddr,
38 get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr,
39 commands: vk::commands::EntryCommands,
40}
41
42impl Entry {
43 pub unsafe fn new(loader: impl Loader + 'static) -> Result<Self, LoadError> {
63 let loader: Arc<dyn Loader> = Arc::new(loader);
64
65 let get_instance_proc_addr: vk::commands::PFN_vkGetInstanceProcAddr = unsafe {
67 let ptr = loader.load(c"vkGetInstanceProcAddr");
68 if ptr.is_null() {
69 return Err(LoadError::MissingEntryPoint);
70 }
71 std::mem::transmute(ptr)
72 };
73
74 let get_instance_proc_addr_fn =
75 get_instance_proc_addr.expect("vkGetInstanceProcAddr not loaded");
76 let null_instance = vk::handles::Instance::null();
77
78 let get_device_proc_addr: vk::commands::PFN_vkGetDeviceProcAddr =
80 unsafe { std::mem::transmute(loader.load(c"vkGetDeviceProcAddr")) };
81
82 let commands = unsafe {
84 vk::commands::EntryCommands::load(|name| {
85 std::mem::transmute(get_instance_proc_addr_fn(null_instance, name.as_ptr()))
86 })
87 };
88
89 Ok(Self {
90 _loader: loader,
91 get_instance_proc_addr,
92 get_device_proc_addr,
93 commands,
94 })
95 }
96
97 pub fn get_instance_proc_addr(&self) -> vk::commands::PFN_vkGetInstanceProcAddr {
102 self.get_instance_proc_addr
103 }
104
105 pub fn get_device_proc_addr(&self) -> vk::commands::PFN_vkGetDeviceProcAddr {
107 self.get_device_proc_addr
108 }
109
110 pub(crate) fn commands(&self) -> &vk::commands::EntryCommands {
112 &self.commands
113 }
114
115 pub fn version(&self) -> VkResult<Version> {
129 let fp = match self.commands.enumerate_instance_version {
130 Some(fp) => fp,
131 None => {
132 return Ok(Version {
133 major: 1,
134 minor: 0,
135 patch: 0,
136 });
137 }
138 };
139 let mut raw = 0u32;
140 check(unsafe { fp(&mut raw) })?;
142 Ok(Version::from_raw(raw))
143 }
144
145 pub unsafe fn create_instance(
172 &self,
173 create_info: &vk::structs::InstanceCreateInfo,
174 allocator: Option<&vk::structs::AllocationCallbacks>,
175 ) -> VkResult<Instance> {
176 let raw = unsafe { self.create_instance_raw(create_info, allocator) }?;
178 let instance = unsafe {
180 Instance::load(
181 raw,
182 self.get_instance_proc_addr,
183 self.get_device_proc_addr,
184 Some(self._loader.clone()),
185 )
186 };
187 Ok(instance)
188 }
189
190 pub unsafe fn create_instance_raw(
202 &self,
203 create_info: &vk::structs::InstanceCreateInfo,
204 allocator: Option<&vk::structs::AllocationCallbacks>,
205 ) -> VkResult<vk::handles::Instance> {
206 let fp = self
207 .commands
208 .create_instance
209 .expect("vkCreateInstance not loaded");
210 let mut instance = vk::handles::Instance::null();
211 let result = unsafe {
213 fp(
214 create_info,
215 allocator.map_or(std::ptr::null(), |a| a),
216 &mut instance,
217 )
218 };
219 check(result)?;
220 Ok(instance)
221 }
222
223 pub unsafe fn enumerate_instance_layer_properties(
229 &self,
230 ) -> VkResult<Vec<vk::structs::LayerProperties>> {
231 let fp = self
232 .commands
233 .enumerate_instance_layer_properties
234 .expect("vkEnumerateInstanceLayerProperties not loaded");
235 enumerate_two_call(|count, data| unsafe { fp(count, data) })
237 }
238
239 pub unsafe fn enumerate_instance_extension_properties(
249 &self,
250 layer_name: Option<&CStr>,
251 ) -> VkResult<Vec<vk::structs::ExtensionProperties>> {
252 let fp = self
253 .commands
254 .enumerate_instance_extension_properties
255 .expect("vkEnumerateInstanceExtensionProperties not loaded");
256 let layer_ptr = layer_name.map_or(std::ptr::null(), |n| n.as_ptr());
257 enumerate_two_call(|count, data| unsafe { fp(layer_ptr, count, data) })
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use std::ffi::{CStr, c_char, c_void};
266
267 struct NullLoader;
269
270 unsafe impl Loader for NullLoader {
271 unsafe fn load(&self, _name: &CStr) -> *const c_void {
272 std::ptr::null()
273 }
274 }
275
276 struct FakeEntryLoader;
280
281 unsafe extern "system" fn mock_get_instance_proc_addr(
282 _instance: vk::handles::Instance,
283 _name: *const c_char,
284 ) -> vk::structs::PFN_vkVoidFunction {
285 None
286 }
287
288 unsafe impl Loader for FakeEntryLoader {
289 unsafe fn load(&self, name: &CStr) -> *const c_void {
290 if name == c"vkGetInstanceProcAddr" {
291 mock_get_instance_proc_addr as *const c_void
292 } else {
293 std::ptr::null()
294 }
295 }
296 }
297
298 #[test]
299 fn new_returns_missing_entry_point_when_loader_returns_null() {
300 let result = unsafe { Entry::new(NullLoader) };
301 match result {
302 Err(LoadError::MissingEntryPoint) => {}
303 Err(other) => panic!("expected MissingEntryPoint, got {other}"),
304 Ok(_) => panic!("expected error, got Ok"),
305 }
306 }
307
308 #[test]
309 fn new_succeeds_with_fake_loader() {
310 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
311 assert!(entry.get_instance_proc_addr().is_some());
312 }
313
314 #[test]
315 fn version_returns_1_0_when_enumerate_instance_version_is_none() {
316 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
319 let version = entry.version().expect("version should succeed");
320 assert_eq!(version.major, 1);
321 assert_eq!(version.minor, 0);
322 assert_eq!(version.patch, 0);
323 }
324
325 #[test]
326 fn commands_returns_reference() {
327 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
328 let _ = entry.commands();
329 }
330
331 #[test]
332 fn get_instance_proc_addr_returns_some() {
333 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
334 assert!(entry.get_instance_proc_addr().is_some());
335 }
336
337 #[test]
338 fn get_device_proc_addr_returns_none_from_fake_loader() {
339 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
341 assert!(entry.get_device_proc_addr().is_none());
342 }
343
344 struct RichEntryLoader;
349
350 unsafe extern "system" fn rich_get_instance_proc_addr(
351 _instance: vk::handles::Instance,
352 name: *const c_char,
353 ) -> vk::structs::PFN_vkVoidFunction {
354 let name = unsafe { CStr::from_ptr(name) };
355 match name.to_bytes() {
356 b"vkEnumerateInstanceVersion" => Some(unsafe {
357 std::mem::transmute::<
358 unsafe extern "system" fn(*mut u32) -> vk::enums::Result,
359 unsafe extern "system" fn(),
360 >(mock_enumerate_instance_version)
361 }),
362 b"vkEnumerateInstanceLayerProperties" => Some(unsafe {
363 std::mem::transmute::<
364 unsafe extern "system" fn(
365 *mut u32,
366 *mut vk::structs::LayerProperties,
367 ) -> vk::enums::Result,
368 unsafe extern "system" fn(),
369 >(mock_enumerate_instance_layer_properties)
370 }),
371 b"vkEnumerateInstanceExtensionProperties" => Some(unsafe {
372 std::mem::transmute::<
373 unsafe extern "system" fn(
374 *const c_char,
375 *mut u32,
376 *mut vk::structs::ExtensionProperties,
377 ) -> vk::enums::Result,
378 unsafe extern "system" fn(),
379 >(mock_enumerate_instance_extension_properties)
380 }),
381 b"vkCreateInstance" => Some(unsafe {
382 std::mem::transmute::<
383 unsafe extern "system" fn(
384 *const vk::structs::InstanceCreateInfo,
385 *const vk::structs::AllocationCallbacks,
386 *mut vk::handles::Instance,
387 ) -> vk::enums::Result,
388 unsafe extern "system" fn(),
389 >(mock_create_instance)
390 }),
391 _ => None,
392 }
393 }
394
395 unsafe extern "system" fn mock_enumerate_instance_version(
396 p_api_version: *mut u32,
397 ) -> vk::enums::Result {
398 unsafe { *p_api_version = (1 << 22) | (3 << 12) | 290 };
400 vk::enums::Result::SUCCESS
401 }
402
403 unsafe extern "system" fn mock_enumerate_instance_layer_properties(
404 p_count: *mut u32,
405 _p_properties: *mut vk::structs::LayerProperties,
406 ) -> vk::enums::Result {
407 unsafe { *p_count = 0 };
408 vk::enums::Result::SUCCESS
409 }
410
411 unsafe extern "system" fn mock_enumerate_instance_extension_properties(
412 _p_layer_name: *const c_char,
413 p_count: *mut u32,
414 _p_properties: *mut vk::structs::ExtensionProperties,
415 ) -> vk::enums::Result {
416 unsafe { *p_count = 0 };
417 vk::enums::Result::SUCCESS
418 }
419
420 unsafe extern "system" fn mock_create_instance(
421 _p_create_info: *const vk::structs::InstanceCreateInfo,
422 _p_allocator: *const vk::structs::AllocationCallbacks,
423 p_instance: *mut vk::handles::Instance,
424 ) -> vk::enums::Result {
425 unsafe { *p_instance = std::mem::transmute::<usize, vk::handles::Instance>(0x1234_usize) };
427 vk::enums::Result::SUCCESS
428 }
429
430 unsafe impl Loader for RichEntryLoader {
431 unsafe fn load(&self, name: &CStr) -> *const c_void {
432 match name.to_bytes() {
433 b"vkGetInstanceProcAddr" => rich_get_instance_proc_addr as *const c_void,
434 b"vkGetDeviceProcAddr" => std::ptr::null(),
435 _ => std::ptr::null(),
436 }
437 }
438 }
439
440 #[test]
441 fn version_returns_parsed_version_when_fp_available() {
442 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
443 let version = entry.version().expect("version should succeed");
444 assert_eq!(version.major, 1);
445 assert_eq!(version.minor, 3);
446 assert_eq!(version.patch, 290);
447 }
448
449 #[test]
450 fn enumerate_layer_properties_with_mock() {
451 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
452 let layers =
453 unsafe { entry.enumerate_instance_layer_properties() }.expect("should succeed");
454 assert!(layers.is_empty());
455 }
456
457 #[test]
458 fn enumerate_extension_properties_with_mock() {
459 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
460 let extensions =
461 unsafe { entry.enumerate_instance_extension_properties(None) }.expect("should succeed");
462 assert!(extensions.is_empty());
463 }
464
465 #[test]
466 fn enumerate_extension_properties_with_layer_name() {
467 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
468 let extensions =
469 unsafe { entry.enumerate_instance_extension_properties(Some(c"VK_LAYER_test")) }
470 .expect("should succeed");
471 assert!(extensions.is_empty());
472 }
473
474 #[test]
475 fn create_instance_raw_with_mock() {
476 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
477 let create_info: vk::structs::InstanceCreateInfo = unsafe { std::mem::zeroed() };
478 let raw = unsafe { entry.create_instance_raw(&create_info, None) }.expect("should succeed");
479 assert!(!raw.is_null());
480 }
481
482 struct FailingEntryLoader;
486
487 unsafe extern "system" fn failing_get_instance_proc_addr(
488 _instance: vk::handles::Instance,
489 name: *const c_char,
490 ) -> vk::structs::PFN_vkVoidFunction {
491 let name = unsafe { CStr::from_ptr(name) };
492 match name.to_bytes() {
493 b"vkEnumerateInstanceVersion" => Some(unsafe {
494 std::mem::transmute::<
495 unsafe extern "system" fn(*mut u32) -> vk::enums::Result,
496 unsafe extern "system" fn(),
497 >(failing_enumerate_instance_version)
498 }),
499 b"vkEnumerateInstanceLayerProperties" => Some(unsafe {
500 std::mem::transmute::<
501 unsafe extern "system" fn(
502 *mut u32,
503 *mut vk::structs::LayerProperties,
504 ) -> vk::enums::Result,
505 unsafe extern "system" fn(),
506 >(failing_enumerate_instance_layer_properties)
507 }),
508 b"vkEnumerateInstanceExtensionProperties" => Some(unsafe {
509 std::mem::transmute::<
510 unsafe extern "system" fn(
511 *const c_char,
512 *mut u32,
513 *mut vk::structs::ExtensionProperties,
514 ) -> vk::enums::Result,
515 unsafe extern "system" fn(),
516 >(failing_enumerate_instance_extension_properties)
517 }),
518 b"vkCreateInstance" => Some(unsafe {
519 std::mem::transmute::<
520 unsafe extern "system" fn(
521 *const vk::structs::InstanceCreateInfo,
522 *const vk::structs::AllocationCallbacks,
523 *mut vk::handles::Instance,
524 ) -> vk::enums::Result,
525 unsafe extern "system" fn(),
526 >(failing_create_instance)
527 }),
528 _ => None,
529 }
530 }
531
532 unsafe extern "system" fn failing_enumerate_instance_version(
533 _p_api_version: *mut u32,
534 ) -> vk::enums::Result {
535 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
536 }
537
538 unsafe extern "system" fn failing_enumerate_instance_layer_properties(
539 _p_count: *mut u32,
540 _p_properties: *mut vk::structs::LayerProperties,
541 ) -> vk::enums::Result {
542 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
543 }
544
545 unsafe extern "system" fn failing_enumerate_instance_extension_properties(
546 _p_layer_name: *const c_char,
547 _p_count: *mut u32,
548 _p_properties: *mut vk::structs::ExtensionProperties,
549 ) -> vk::enums::Result {
550 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
551 }
552
553 unsafe extern "system" fn failing_create_instance(
554 _p_create_info: *const vk::structs::InstanceCreateInfo,
555 _p_allocator: *const vk::structs::AllocationCallbacks,
556 _p_instance: *mut vk::handles::Instance,
557 ) -> vk::enums::Result {
558 vk::enums::Result::ERROR_INITIALIZATION_FAILED
559 }
560
561 unsafe impl Loader for FailingEntryLoader {
562 unsafe fn load(&self, name: &CStr) -> *const c_void {
563 match name.to_bytes() {
564 b"vkGetInstanceProcAddr" => failing_get_instance_proc_addr as *const c_void,
565 _ => std::ptr::null(),
566 }
567 }
568 }
569
570 #[test]
571 fn version_propagates_error() {
572 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
573 let result = entry.version();
574 assert_eq!(
575 result.unwrap_err(),
576 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
577 );
578 }
579
580 #[test]
581 fn create_instance_raw_propagates_error() {
582 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
583 let create_info: vk::structs::InstanceCreateInfo = unsafe { std::mem::zeroed() };
584 let result = unsafe { entry.create_instance_raw(&create_info, None) };
585 assert_eq!(
586 result.unwrap_err(),
587 vk::enums::Result::ERROR_INITIALIZATION_FAILED
588 );
589 }
590
591 #[test]
592 fn enumerate_layer_properties_propagates_error() {
593 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
594 let result = unsafe { entry.enumerate_instance_layer_properties() };
595 assert_eq!(
596 result.unwrap_err(),
597 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
598 );
599 }
600
601 #[test]
602 fn enumerate_extension_properties_propagates_error() {
603 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
604 let result = unsafe { entry.enumerate_instance_extension_properties(None) };
605 assert_eq!(
606 result.unwrap_err(),
607 vk::enums::Result::ERROR_OUT_OF_HOST_MEMORY
608 );
609 }
610
611 #[test]
612 #[ignore] fn new_succeeds_with_real_loader() {
614 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
615 let loader = crate::loader::LibloadingLoader::new().expect("failed to load Vulkan library");
616 let entry = unsafe { Entry::new(loader) }.expect("failed to create Entry");
617 assert!(entry.get_instance_proc_addr().is_some());
618 assert!(entry.get_device_proc_addr().is_some());
619 }
620
621 #[test]
622 #[ignore] fn version_returns_at_least_1_0() {
624 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
625 let entry = create_entry();
626 let version = entry.version().expect("failed to query version");
627 assert!(version.major >= 1);
628 println!("Vulkan {version}");
629 }
630
631 #[test]
632 #[ignore] fn enumerate_layer_properties_succeeds() {
634 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
635 let entry = create_entry();
636 let layers = unsafe { entry.enumerate_instance_layer_properties() }
637 .expect("failed to enumerate layers");
638 println!("found {} layers", layers.len());
639 }
640
641 #[test]
642 #[ignore] fn enumerate_extension_properties_succeeds() {
644 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
645 let entry = create_entry();
646 let extensions = unsafe { entry.enumerate_instance_extension_properties(None) }
647 .expect("failed to enumerate extensions");
648 assert!(!extensions.is_empty(), "expected at least one extension");
649 println!("found {} extensions", extensions.len());
650 }
651
652 fn create_entry() -> Entry {
654 let loader = crate::loader::LibloadingLoader::new().expect("failed to load Vulkan library");
655 unsafe { Entry::new(loader) }.expect("failed to create Entry")
656 }
657}