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::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::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::InstanceCreateInfo,
174 allocator: Option<&vk::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::InstanceCreateInfo,
204 allocator: Option<&vk::AllocationCallbacks>,
205 ) -> VkResult<vk::Instance> {
206 let fp = self
207 .commands
208 .create_instance
209 .expect("vkCreateInstance not loaded");
210 let mut instance = vk::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(&self) -> VkResult<Vec<vk::LayerProperties>> {
229 let fp = self
230 .commands
231 .enumerate_instance_layer_properties
232 .expect("vkEnumerateInstanceLayerProperties not loaded");
233 enumerate_two_call(|count, data| unsafe { fp(count, data) })
235 }
236
237 pub unsafe fn enumerate_instance_extension_properties(
247 &self,
248 layer_name: Option<&CStr>,
249 ) -> VkResult<Vec<vk::ExtensionProperties>> {
250 let fp = self
251 .commands
252 .enumerate_instance_extension_properties
253 .expect("vkEnumerateInstanceExtensionProperties not loaded");
254 let layer_ptr = layer_name.map_or(std::ptr::null(), |n| n.as_ptr());
255 enumerate_two_call(|count, data| unsafe { fp(layer_ptr, count, data) })
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263 use std::ffi::{CStr, c_char, c_void};
264
265 struct NullLoader;
267
268 unsafe impl Loader for NullLoader {
269 unsafe fn load(&self, _name: &CStr) -> *const c_void {
270 std::ptr::null()
271 }
272 }
273
274 struct FakeEntryLoader;
278
279 unsafe extern "system" fn mock_get_instance_proc_addr(
280 _instance: vk::Instance,
281 _name: *const c_char,
282 ) -> vk::PFN_vkVoidFunction {
283 None
284 }
285
286 unsafe impl Loader for FakeEntryLoader {
287 unsafe fn load(&self, name: &CStr) -> *const c_void {
288 if name == c"vkGetInstanceProcAddr" {
289 mock_get_instance_proc_addr as *const c_void
290 } else {
291 std::ptr::null()
292 }
293 }
294 }
295
296 #[test]
297 fn new_returns_missing_entry_point_when_loader_returns_null() {
298 let result = unsafe { Entry::new(NullLoader) };
299 match result {
300 Err(LoadError::MissingEntryPoint) => {}
301 Err(other) => panic!("expected MissingEntryPoint, got {other}"),
302 Ok(_) => panic!("expected error, got Ok"),
303 }
304 }
305
306 #[test]
307 fn new_succeeds_with_fake_loader() {
308 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
309 assert!(entry.get_instance_proc_addr().is_some());
310 }
311
312 #[test]
313 fn version_returns_1_0_when_enumerate_instance_version_is_none() {
314 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
317 let version = entry.version().expect("version should succeed");
318 assert_eq!(version.major, 1);
319 assert_eq!(version.minor, 0);
320 assert_eq!(version.patch, 0);
321 }
322
323 #[test]
324 fn commands_returns_reference() {
325 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
326 let _ = entry.commands();
327 }
328
329 #[test]
330 fn get_instance_proc_addr_returns_some() {
331 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
332 assert!(entry.get_instance_proc_addr().is_some());
333 }
334
335 #[test]
336 fn get_device_proc_addr_returns_none_from_fake_loader() {
337 let entry = unsafe { Entry::new(FakeEntryLoader) }.expect("should create Entry");
339 assert!(entry.get_device_proc_addr().is_none());
340 }
341
342 struct RichEntryLoader;
347
348 unsafe extern "system" fn rich_get_instance_proc_addr(
349 _instance: vk::Instance,
350 name: *const c_char,
351 ) -> vk::PFN_vkVoidFunction {
352 let name = unsafe { CStr::from_ptr(name) };
353 match name.to_bytes() {
354 b"vkEnumerateInstanceVersion" => Some(unsafe {
355 std::mem::transmute::<
356 unsafe extern "system" fn(*mut u32) -> vk::Result,
357 unsafe extern "system" fn(),
358 >(mock_enumerate_instance_version)
359 }),
360 b"vkEnumerateInstanceLayerProperties" => Some(unsafe {
361 std::mem::transmute::<
362 unsafe extern "system" fn(*mut u32, *mut vk::LayerProperties) -> vk::Result,
363 unsafe extern "system" fn(),
364 >(mock_enumerate_instance_layer_properties)
365 }),
366 b"vkEnumerateInstanceExtensionProperties" => Some(unsafe {
367 std::mem::transmute::<
368 unsafe extern "system" fn(
369 *const c_char,
370 *mut u32,
371 *mut vk::ExtensionProperties,
372 ) -> vk::Result,
373 unsafe extern "system" fn(),
374 >(mock_enumerate_instance_extension_properties)
375 }),
376 b"vkCreateInstance" => Some(unsafe {
377 std::mem::transmute::<
378 unsafe extern "system" fn(
379 *const vk::InstanceCreateInfo,
380 *const vk::AllocationCallbacks,
381 *mut vk::Instance,
382 ) -> vk::Result,
383 unsafe extern "system" fn(),
384 >(mock_create_instance)
385 }),
386 _ => None,
387 }
388 }
389
390 unsafe extern "system" fn mock_enumerate_instance_version(
391 p_api_version: *mut u32,
392 ) -> vk::Result {
393 unsafe { *p_api_version = crate::Version::new(1, 3, 290).to_raw() };
394 vk::Result::SUCCESS
395 }
396
397 unsafe extern "system" fn mock_enumerate_instance_layer_properties(
398 p_count: *mut u32,
399 _p_properties: *mut vk::LayerProperties,
400 ) -> vk::Result {
401 unsafe { *p_count = 0 };
402 vk::Result::SUCCESS
403 }
404
405 unsafe extern "system" fn mock_enumerate_instance_extension_properties(
406 _p_layer_name: *const c_char,
407 p_count: *mut u32,
408 _p_properties: *mut vk::ExtensionProperties,
409 ) -> vk::Result {
410 unsafe { *p_count = 0 };
411 vk::Result::SUCCESS
412 }
413
414 unsafe extern "system" fn mock_create_instance(
415 _p_create_info: *const vk::InstanceCreateInfo,
416 _p_allocator: *const vk::AllocationCallbacks,
417 p_instance: *mut vk::Instance,
418 ) -> vk::Result {
419 unsafe { *p_instance = std::mem::transmute::<usize, vk::Instance>(0x1234_usize) };
421 vk::Result::SUCCESS
422 }
423
424 unsafe impl Loader for RichEntryLoader {
425 unsafe fn load(&self, name: &CStr) -> *const c_void {
426 match name.to_bytes() {
427 b"vkGetInstanceProcAddr" => rich_get_instance_proc_addr as *const c_void,
428 b"vkGetDeviceProcAddr" => std::ptr::null(),
429 _ => std::ptr::null(),
430 }
431 }
432 }
433
434 #[test]
435 fn version_returns_parsed_version_when_fp_available() {
436 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
437 let version = entry.version().expect("version should succeed");
438 assert_eq!(version.major, 1);
439 assert_eq!(version.minor, 3);
440 assert_eq!(version.patch, 290);
441 }
442
443 #[test]
444 fn enumerate_layer_properties_with_mock() {
445 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
446 let layers =
447 unsafe { entry.enumerate_instance_layer_properties() }.expect("should succeed");
448 assert!(layers.is_empty());
449 }
450
451 #[test]
452 fn enumerate_extension_properties_with_mock() {
453 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
454 let extensions =
455 unsafe { entry.enumerate_instance_extension_properties(None) }.expect("should succeed");
456 assert!(extensions.is_empty());
457 }
458
459 #[test]
460 fn enumerate_extension_properties_with_layer_name() {
461 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
462 let extensions =
463 unsafe { entry.enumerate_instance_extension_properties(Some(c"VK_LAYER_test")) }
464 .expect("should succeed");
465 assert!(extensions.is_empty());
466 }
467
468 #[test]
469 fn create_instance_raw_with_mock() {
470 let entry = unsafe { Entry::new(RichEntryLoader) }.expect("should create Entry");
471 let create_info: vk::InstanceCreateInfo = unsafe { std::mem::zeroed() };
472 let raw = unsafe { entry.create_instance_raw(&create_info, None) }.expect("should succeed");
473 assert!(!raw.is_null());
474 }
475
476 struct FailingEntryLoader;
480
481 unsafe extern "system" fn failing_get_instance_proc_addr(
482 _instance: vk::Instance,
483 name: *const c_char,
484 ) -> vk::PFN_vkVoidFunction {
485 let name = unsafe { CStr::from_ptr(name) };
486 match name.to_bytes() {
487 b"vkEnumerateInstanceVersion" => Some(unsafe {
488 std::mem::transmute::<
489 unsafe extern "system" fn(*mut u32) -> vk::Result,
490 unsafe extern "system" fn(),
491 >(failing_enumerate_instance_version)
492 }),
493 b"vkEnumerateInstanceLayerProperties" => Some(unsafe {
494 std::mem::transmute::<
495 unsafe extern "system" fn(*mut u32, *mut vk::LayerProperties) -> vk::Result,
496 unsafe extern "system" fn(),
497 >(failing_enumerate_instance_layer_properties)
498 }),
499 b"vkEnumerateInstanceExtensionProperties" => Some(unsafe {
500 std::mem::transmute::<
501 unsafe extern "system" fn(
502 *const c_char,
503 *mut u32,
504 *mut vk::ExtensionProperties,
505 ) -> vk::Result,
506 unsafe extern "system" fn(),
507 >(failing_enumerate_instance_extension_properties)
508 }),
509 b"vkCreateInstance" => Some(unsafe {
510 std::mem::transmute::<
511 unsafe extern "system" fn(
512 *const vk::InstanceCreateInfo,
513 *const vk::AllocationCallbacks,
514 *mut vk::Instance,
515 ) -> vk::Result,
516 unsafe extern "system" fn(),
517 >(failing_create_instance)
518 }),
519 _ => None,
520 }
521 }
522
523 unsafe extern "system" fn failing_enumerate_instance_version(
524 _p_api_version: *mut u32,
525 ) -> vk::Result {
526 vk::Result::ERROR_OUT_OF_HOST_MEMORY
527 }
528
529 unsafe extern "system" fn failing_enumerate_instance_layer_properties(
530 _p_count: *mut u32,
531 _p_properties: *mut vk::LayerProperties,
532 ) -> vk::Result {
533 vk::Result::ERROR_OUT_OF_HOST_MEMORY
534 }
535
536 unsafe extern "system" fn failing_enumerate_instance_extension_properties(
537 _p_layer_name: *const c_char,
538 _p_count: *mut u32,
539 _p_properties: *mut vk::ExtensionProperties,
540 ) -> vk::Result {
541 vk::Result::ERROR_OUT_OF_HOST_MEMORY
542 }
543
544 unsafe extern "system" fn failing_create_instance(
545 _p_create_info: *const vk::InstanceCreateInfo,
546 _p_allocator: *const vk::AllocationCallbacks,
547 _p_instance: *mut vk::Instance,
548 ) -> vk::Result {
549 vk::Result::ERROR_INITIALIZATION_FAILED
550 }
551
552 unsafe impl Loader for FailingEntryLoader {
553 unsafe fn load(&self, name: &CStr) -> *const c_void {
554 match name.to_bytes() {
555 b"vkGetInstanceProcAddr" => failing_get_instance_proc_addr as *const c_void,
556 _ => std::ptr::null(),
557 }
558 }
559 }
560
561 #[test]
562 fn version_propagates_error() {
563 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
564 let result = entry.version();
565 assert_eq!(result.unwrap_err(), vk::Result::ERROR_OUT_OF_HOST_MEMORY);
566 }
567
568 #[test]
569 fn create_instance_raw_propagates_error() {
570 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
571 let create_info: vk::InstanceCreateInfo = unsafe { std::mem::zeroed() };
572 let result = unsafe { entry.create_instance_raw(&create_info, None) };
573 assert_eq!(result.unwrap_err(), vk::Result::ERROR_INITIALIZATION_FAILED);
574 }
575
576 #[test]
577 fn enumerate_layer_properties_propagates_error() {
578 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
579 let result = unsafe { entry.enumerate_instance_layer_properties() };
580 assert_eq!(result.unwrap_err(), vk::Result::ERROR_OUT_OF_HOST_MEMORY);
581 }
582
583 #[test]
584 fn enumerate_extension_properties_propagates_error() {
585 let entry = unsafe { Entry::new(FailingEntryLoader) }.expect("should create Entry");
586 let result = unsafe { entry.enumerate_instance_extension_properties(None) };
587 assert_eq!(result.unwrap_err(), vk::Result::ERROR_OUT_OF_HOST_MEMORY);
588 }
589
590 #[test]
591 #[ignore] fn new_succeeds_with_real_loader() {
593 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
594 let loader = crate::loader::LibloadingLoader::new().expect("failed to load Vulkan library");
595 let entry = unsafe { Entry::new(loader) }.expect("failed to create Entry");
596 assert!(entry.get_instance_proc_addr().is_some());
597 assert!(entry.get_device_proc_addr().is_some());
598 }
599
600 #[test]
601 #[ignore] fn version_returns_at_least_1_0() {
603 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
604 let entry = create_entry();
605 let version = entry.version().expect("failed to query version");
606 assert!(version.major >= 1);
607 println!("Vulkan {version}");
608 }
609
610 #[test]
611 #[ignore] fn enumerate_layer_properties_succeeds() {
613 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
614 let entry = create_entry();
615 let layers = unsafe { entry.enumerate_instance_layer_properties() }
616 .expect("failed to enumerate layers");
617 println!("found {} layers", layers.len());
618 }
619
620 #[test]
621 #[ignore] fn enumerate_extension_properties_succeeds() {
623 let _vk = crate::VK_TEST_MUTEX.lock().expect("VK_TEST_MUTEX poisoned");
624 let entry = create_entry();
625 let extensions = unsafe { entry.enumerate_instance_extension_properties(None) }
626 .expect("failed to enumerate extensions");
627 assert!(!extensions.is_empty(), "expected at least one extension");
628 println!("found {} extensions", extensions.len());
629 }
630
631 fn create_entry() -> Entry {
633 let loader = crate::loader::LibloadingLoader::new().expect("failed to load Vulkan library");
634 unsafe { Entry::new(loader) }.expect("failed to create Entry")
635 }
636}