1use {
4 super::{DriverError, physical_device::PhysicalDevice},
5 ash::{ext, khr, vk, vk::Handle},
6 derive_builder::{Builder, UninitializedFieldError},
7 log::{debug, error, trace, warn},
8 raw_window_handle::{HasDisplayHandle, RawDisplayHandle},
9 std::{
10 error::Error,
11 ffi::CStr,
12 fmt::{Debug, Display, Formatter},
13 ops::Deref,
14 sync::Arc,
15 thread::panicking,
16 },
17};
18
19#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
20use {
21 log::{Level, Metadata, info, logger},
22 std::{
23 env::var,
24 ffi::c_void,
25 process::{abort, id},
26 thread::{current, park},
27 },
28};
29
30#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
31const SKIP_VALIDATION_PARK_ENV: &str = "VK_GRAPH_SKIP_VALIDATION_PARK";
32
33#[cfg(target_os = "macos")]
34use std::env::set_var;
35
36#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
37unsafe extern "system" fn debug_callback(
38 message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
39 _message_types: vk::DebugUtilsMessageTypeFlagsEXT,
40 callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
41 _user_data: *mut c_void,
42) -> vk::Bool32 {
43 if panicking() {
44 return vk::FALSE;
45 }
46
47 assert!(!callback_data.is_null());
48
49 let callback_data = unsafe { &*callback_data };
50 let message = if callback_data.p_message.is_null() {
51 "<missing Vulkan validation message>"
52 } else {
53 unsafe { CStr::from_ptr(callback_data.p_message) }
54 .to_str()
55 .unwrap_or("<invalid Vulkan validation message>")
56 };
57
58 if !callback_data.p_message_id_name.is_null() {
59 let vuid = unsafe { CStr::from_ptr(callback_data.p_message_id_name) }
60 .to_str()
61 .unwrap_or("<invalid Vulkan validation message ID name>");
62 if vuid != "Loader Message" {
63 debug!("{vuid}");
64 }
65 };
66
67 let is_error = message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR);
68
69 if is_error
74 && message.contains("THREADING ERROR")
75 && message.contains("VkQueue is simultaneously used")
76 {
77 info!("ignoring: {message}");
78
79 return vk::FALSE;
80 }
81
82 if is_error {
83 error!("{message}");
84 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) {
85 warn!("{message}");
86 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::INFO) {
87 info!("{message}");
88 } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE) {
89 debug!("{message}");
90 }
91
92 if !is_error {
93 return vk::FALSE;
94 }
95
96 if !logger().enabled(&Metadata::builder().level(Level::Debug).build())
97 || var("RUST_LOG")
98 .map(|rust_log| rust_log.is_empty())
99 .unwrap_or(true)
100 {
101 eprintln!(
102 "note: run with `RUST_LOG=trace` environment variable to display more information"
103 );
104 eprintln!("note: see https://github.com/rust-lang/log#in-executables");
105 abort()
106 }
107
108 if current().name() != Some("main") {
109 warn!("invalid validation callback thread: child thread")
110 }
111
112 if var(SKIP_VALIDATION_PARK_ENV)
113 .map(|value| !matches!(value.as_str(), "" | "0" | "false" | "False" | "FALSE"))
114 .unwrap_or(false)
115 {
116 warn!("validation callback park skipped; execution will continue");
117 logger().flush();
118
119 return vk::FALSE;
120 }
121
122 debug!(
123 "parking validation callback thread `{}` for debugger attach to pid {}",
124 current().name().unwrap_or_default(),
125 id()
126 );
127
128 logger().flush();
129 park();
130
131 vk::FALSE
132}
133
134#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
135fn debug_extension_names() -> &'static [&'static CStr] {
136 &[ext::debug_utils::NAME]
137}
138
139#[cfg(all(target_os = "macos", not(feature = "loaded")))]
140fn debug_extension_names() -> &'static [&'static CStr] {
141 &[]
142}
143
144#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
145fn debug_layer_names() -> &'static [&'static CStr] {
146 &[c"VK_LAYER_KHRONOS_validation"]
147}
148
149#[cfg(all(target_os = "macos", not(feature = "loaded")))]
150fn debug_layer_names() -> &'static [&'static CStr] {
151 &[]
152}
153
154fn display_extension_names(
156 display_handle: RawDisplayHandle,
157) -> Result<&'static [&'static CStr], DriverError> {
158 let extensions = match display_handle {
159 RawDisplayHandle::Windows(_) => &[khr::surface::NAME, khr::win32_surface::NAME],
160 RawDisplayHandle::Wayland(_) => &[khr::surface::NAME, khr::wayland_surface::NAME],
161 RawDisplayHandle::Xlib(_) => &[khr::surface::NAME, khr::xlib_surface::NAME],
162 RawDisplayHandle::Xcb(_) => &[khr::surface::NAME, khr::xcb_surface::NAME],
163 RawDisplayHandle::Android(_) => &[khr::surface::NAME, khr::android_surface::NAME],
164 RawDisplayHandle::AppKit(_) | RawDisplayHandle::UiKit(_) => {
165 &[khr::surface::NAME, ext::metal_surface::NAME]
166 }
167 _ => {
168 warn!("unsupported display handle type: {display_handle:?}");
169
170 return Err(DriverError::Unsupported);
171 }
172 };
173
174 Ok(extensions)
175}
176
177fn has_surface_ext(entry: &ash::Entry, instance: vk::Instance) -> bool {
182 [
183 c"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
184 c"vkGetPhysicalDeviceSurfaceFormatsKHR",
185 c"vkGetPhysicalDeviceSurfacePresentModesKHR",
186 c"vkGetPhysicalDeviceSurfaceSupportKHR",
187 c"vkDestroySurfaceKHR",
188 ]
189 .into_iter()
190 .all(|name| unsafe {
191 entry
192 .get_instance_proc_addr(instance, name.as_ptr())
193 .is_some()
194 })
195}
196
197#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
199pub enum ApiVersion {
200 #[default]
202 Vulkan12,
203
204 Vulkan13,
206}
207
208impl ApiVersion {
209 pub fn try_parse_vk_api_version(version: u32) -> Result<Self, ParseApiVersionError> {
211 Self::try_from(version)
212 }
213
214 pub fn major(self) -> u32 {
218 1
219 }
220
221 pub fn minor(self) -> u32 {
223 match self {
224 Self::Vulkan12 => 2,
225 Self::Vulkan13 => 3,
226 }
227 }
228
229 pub fn patch(self) -> u32 {
233 0
234 }
235
236 pub fn to_vk_api_version(self) -> u32 {
238 self.into()
239 }
240
241 pub fn variant(self) -> u32 {
245 0
246 }
247}
248
249impl From<ApiVersion> for u32 {
250 fn from(val: ApiVersion) -> Self {
251 vk::make_api_version(val.variant(), val.major(), val.minor(), val.patch())
252 }
253}
254
255impl TryFrom<u32> for ApiVersion {
256 type Error = ParseApiVersionError;
257
258 fn try_from(val: u32) -> Result<Self, Self::Error> {
259 let major = vk::api_version_major(val);
260 let minor = vk::api_version_minor(val);
261 let patch = vk::api_version_patch(val);
262 let variant = vk::api_version_variant(val);
263
264 if variant != 0 || major != 1 || minor < 2 {
265 return Err(ParseApiVersionError {
266 major,
267 minor,
268 patch,
269 variant,
270 });
271 }
272
273 Ok(match minor {
274 2 => ApiVersion::Vulkan12,
275 _ => ApiVersion::Vulkan13,
276 })
277 }
278}
279
280#[read_only::embed]
286#[allow(private_interfaces)]
287pub struct Instance {
288 #[readonly]
292 pub info: InstanceInfo,
293
294 #[readonly]
295 pub(self) inner: Arc<InstanceInner>,
296
297 #[readonly]
301 pub surface_ext: bool,
302}
303
304impl Clone for Instance {
305 fn clone(&self) -> Self {
306 Self {
307 read_only: ReadOnlyInstance {
308 info: self.info,
309 surface_ext: self.surface_ext,
310 inner: self.inner.clone(),
311 },
312 }
313 }
314}
315
316impl Instance {
317 pub const LATEST_API_VERSION: ApiVersion = ApiVersion::Vulkan13;
319
320 #[deprecated = "use create"]
321 #[doc(hidden)]
322 pub fn new(info: impl Into<InstanceInfo>) -> Result<Self, DriverError> {
323 Self::create(info)
324 }
325
326 #[profiling::function]
332 pub fn create(info: impl Into<InstanceInfo>) -> Result<Self, DriverError> {
333 Self::create_with_extension_names(info.into(), &[])
334 }
335
336 fn create_with_extension_names(
337 info: InstanceInfo,
338 extra_extension_names: &[&CStr],
339 ) -> Result<Self, DriverError> {
340 #[cfg(target_os = "macos")]
342 unsafe {
343 set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1");
344 }
345
346 #[cfg(feature = "loaded")]
348 let entry = unsafe {
349 ash::Entry::load().map_err(|err| {
350 error!("unable to load Vulkan driver: {err}");
351
352 DriverError::Unsupported
353 })?
354 };
355
356 #[cfg(not(feature = "loaded"))]
358 let entry = {
359 #[cfg(not(target_os = "macos"))]
360 let entry = ash::Entry::linked();
361
362 #[cfg(target_os = "macos")]
364 let entry = ash_molten::load();
365 };
366
367 let mut extension_names = info
368 .extension_names
369 .iter()
370 .chain(extra_extension_names)
371 .copied()
372 .collect::<Vec<_>>();
373
374 if info.debug {
375 extension_names.extend(debug_extension_names());
376 }
377
378 #[cfg(all(target_os = "macos", feature = "loaded"))]
382 {
383 extension_names.extend(&[
384 ash::khr::get_physical_device_properties2::NAME,
385 ash::khr::portability_enumeration::NAME,
386 ]);
387 }
388
389 let surface_ext = extension_names.contains(&khr::surface::NAME);
390
391 let extension_name_ptrs = extension_names
392 .iter()
393 .copied()
394 .map(CStr::as_ptr)
395 .collect::<Box<_>>();
396
397 let mut layer_names = Vec::with_capacity(info.debug as _);
398
399 if info.debug {
400 layer_names.extend(debug_layer_names());
401 }
402
403 let layer_name_ptrs = layer_names
404 .iter()
405 .copied()
406 .map(CStr::as_ptr)
407 .collect::<Box<_>>();
408
409 let app_desc =
410 vk::ApplicationInfo::default().api_version(info.api_version.to_vk_api_version());
411 let instance_desc = vk::InstanceCreateInfo::default()
412 .application_info(&app_desc)
413 .enabled_layer_names(&layer_name_ptrs)
414 .enabled_extension_names(&extension_name_ptrs);
415
416 #[cfg(all(target_os = "macos", feature = "loaded"))]
419 let instance_desc = instance_desc.flags(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR);
420
421 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
422 let mut debug_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
423 .message_severity(
424 vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
425 | vk::DebugUtilsMessageSeverityFlagsEXT::INFO
426 | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
427 | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
428 )
429 .message_type(
430 vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
431 | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
432 | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
433 )
434 .pfn_user_callback(Some(debug_callback));
435
436 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
437 let instance_desc = if info.debug {
438 instance_desc.push_next(&mut debug_create_info)
439 } else {
440 instance_desc
441 };
442
443 let instance = unsafe {
444 entry.create_instance(&instance_desc, None).map_err(|_| {
445 if info.debug {
446 warn!("debug may only be enabled with a valid Vulkan SDK installation");
447 }
448
449 error!(
450 "Vulkan driver does not support API v{}",
451 match info.api_version {
452 ApiVersion::Vulkan12 => "1.2",
453 ApiVersion::Vulkan13 => "1.3",
454 }
455 );
456
457 for layer_name in &layer_names {
458 debug!("Layer: {:?}", layer_name);
459 }
460
461 for extension_name in extension_names {
462 debug!("Extension: {:?}", extension_name);
463 }
464
465 DriverError::Unsupported
466 })?
467 };
468
469 trace!("created a Vulkan instance");
470
471 #[cfg(all(target_os = "macos", not(feature = "loaded")))]
472 let debug_utils = None;
473
474 #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
475 let debug_utils = if info.debug {
476 let debug_utils = ext::debug_utils::Instance::new(&entry, &instance);
477 let debug_messenger =
478 unsafe { debug_utils.create_debug_utils_messenger(&debug_create_info, None) }
479 .map_err(|err| {
480 unsafe {
481 instance.destroy_instance(None);
482 }
483
484 error!("unable to create debug utils messenger: {err}");
485
486 DriverError::Unsupported
487 })?;
488
489 Some((debug_utils, debug_messenger))
490 } else {
491 None
492 };
493
494 Ok(Self {
495 read_only: ReadOnlyInstance {
496 info,
497 inner: Arc::new(InstanceInner {
498 debug_utils,
499 entry,
500 instance,
501 instance_created: true,
502 }),
503 surface_ext,
504 },
505 })
506 }
507
508 pub fn entry(this: &Self) -> &ash::Entry {
510 &this.inner.entry
511 }
512
513 #[deprecated = "use try_from_entry"]
514 #[doc(hidden)]
515 pub fn from_entry(entry: ash::Entry, instance: vk::Instance) -> Result<Self, DriverError> {
516 Self::try_from_entry(entry, instance)
517 }
518
519 #[profiling::function]
521 pub fn physical_device(
522 this: &Self,
523 physical_device: vk::PhysicalDevice,
524 ) -> Result<PhysicalDevice, DriverError> {
525 let physical_device = PhysicalDevice::new(this.clone(), physical_device)?;
526 if let Err(err) =
527 ApiVersion::try_parse_vk_api_version(physical_device.properties_v1_0.api_version)
528 {
529 warn!(
530 "unsupported physical device `{}`: {err}",
531 physical_device.properties_v1_0.device_name
532 );
533
534 return Err(DriverError::Unsupported);
535 }
536
537 Ok(physical_device)
538 }
539
540 #[profiling::function]
542 pub fn physical_devices(
543 this: &Self,
544 ) -> Result<impl IntoIterator<Item = PhysicalDevice>, DriverError> {
545 let physical_devices = unsafe { this.enumerate_physical_devices() }.map_err(|err| {
546 error!("unable to enumerate physical devices: {err}");
547
548 match err {
549 vk::Result::ERROR_INITIALIZATION_FAILED => DriverError::Unsupported,
550 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
551 DriverError::OutOfMemory
552 }
553 vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
554 _ => {
555 warn!("unexpected enumerate_physical_devices error: {err}");
556
557 DriverError::Unsupported
558 }
559 }
560 })?;
561
562 Ok(physical_devices
563 .into_iter()
564 .enumerate()
565 .filter_map(|(idx, physical_device)| {
566 let res = PhysicalDevice::new(this.clone(), physical_device);
567
568 if let Err(err) = &res {
569 warn!("unsupported physical device #{idx}: {err}");
570 }
571
572 res.ok().filter(|physical_device| {
573 ApiVersion::try_parse_vk_api_version(
574 physical_device.properties_v1_0.api_version,
575 )
576 .inspect_err(|err| {
577 debug!(
578 "unsupported physical device `{}`: {err}",
579 physical_device.properties_v1_0.device_name
580 );
581 })
582 .is_ok()
583 })
584 }))
585 }
586
587 #[profiling::function]
590 pub fn try_from_display(
591 display: impl HasDisplayHandle,
592 info: impl Into<InstanceInfo>,
593 ) -> Result<Self, DriverError> {
594 let display_handle = display.display_handle().map_err(|err| {
595 warn!("unable to get display handle: {err}");
596
597 DriverError::Unsupported
598 })?;
599 let display_extension_names =
600 display_extension_names(display_handle.as_raw()).map_err(|err| {
601 warn!("unable to enumerate display extensions: {err}");
602
603 DriverError::Unsupported
604 })?;
605
606 Self::create_with_extension_names(info.into(), display_extension_names)
607 }
608
609 #[profiling::function]
614 pub fn try_from_entry(entry: ash::Entry, instance: vk::Instance) -> Result<Self, DriverError> {
615 if instance == vk::Instance::null() {
616 warn!("invalid VkInstance handle: null");
617
618 return Err(DriverError::InvalidData);
619 }
620
621 let api_version = unsafe { entry.try_enumerate_instance_version() }
622 .map_err(|err| match err {
623 vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
624 vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
625 err => {
626 error!("unable to enumerate instance version: {err}");
627
628 DriverError::Unsupported
629 }
630 })?
631 .unwrap_or_else(|| {
632 Self::LATEST_API_VERSION.to_vk_api_version()
634 })
635 .try_into()
636 .map_err(|err| {
637 warn!("unsupported instance: {err}");
638
639 DriverError::Unsupported
640 })?;
641 let surface_ext = has_surface_ext(&entry, instance);
642
643 let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) };
644
645 Ok(Self {
646 read_only: ReadOnlyInstance {
647 info: InstanceInfo {
648 api_version,
649 ..Default::default()
650 },
651 inner: Arc::new(InstanceInner {
652 debug_utils: None,
653 entry,
654 instance,
655 instance_created: false,
656 }),
657 surface_ext,
658 },
659 })
660 }
661}
662
663impl Debug for Instance {
664 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
665 f.write_str("Instance")
666 }
667}
668
669#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
671#[builder(
672 build_fn(private, name = "fallible_build", error = "UninitializedFieldError"),
673 derive(Clone, Copy, Debug),
674 pattern = "owned"
675)]
676pub struct InstanceInfo {
677 #[builder(default = "ApiVersion::Vulkan13")]
679 pub api_version: ApiVersion,
680
681 #[builder(default)]
698 pub debug: bool,
699
700 #[builder(default)]
702 pub extension_names: &'static [&'static CStr],
703}
704
705impl InstanceInfo {
706 pub fn builder() -> InstanceInfoBuilder {
708 Default::default()
709 }
710
711 pub fn into_builder(self) -> InstanceInfoBuilder {
713 InstanceInfoBuilder {
714 api_version: Some(self.api_version),
715 debug: Some(self.debug),
716 extension_names: Some(self.extension_names),
717 }
718 }
719
720 #[deprecated = "use into_builder function"]
721 #[doc(hidden)]
722 pub fn to_builder(self) -> InstanceInfoBuilder {
723 self.into_builder()
724 }
725}
726
727impl InstanceInfoBuilder {
728 #[inline(always)]
730 pub fn build(self) -> InstanceInfo {
731 self.fallible_build().expect("invalid instance info")
732 }
733}
734
735impl From<InstanceInfoBuilder> for InstanceInfo {
736 fn from(info: InstanceInfoBuilder) -> Self {
737 info.build()
738 }
739}
740
741struct InstanceInner {
742 debug_utils: Option<(ext::debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,
743 entry: ash::Entry,
744 instance: ash::Instance,
745 instance_created: bool,
746}
747
748impl Drop for InstanceInner {
749 #[profiling::function]
750 fn drop(&mut self) {
751 if panicking() {
752 return;
753 }
754
755 unsafe {
756 if let Some((debug_utils, debug_messenger)) = self.debug_utils.take() {
757 trace!("destroy debug_utils_messenger {}", debug_messenger.as_raw());
758 debug_utils.destroy_debug_utils_messenger(debug_messenger, None);
759 trace!(
760 "destroy debug_utils_messenger {} DONE",
761 debug_messenger.as_raw()
762 );
763 }
764
765 if self.instance_created {
766 trace!("destroy instance {}", self.instance.handle().as_raw());
767 self.instance.destroy_instance(None);
768 self.instance_created = false;
769 }
770 }
771 }
772}
773
774#[derive(Clone, Copy, Debug)]
776pub struct ParseApiVersionError {
777 pub major: u32,
780
781 pub minor: u32,
784
785 pub patch: u32,
788
789 pub variant: u32,
792}
793
794impl Display for ParseApiVersionError {
795 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
796 f.write_fmt(format_args!(
797 "v{}.{}.{}-{}",
798 self.major, self.minor, self.patch, self.variant
799 ))
800 }
801}
802
803impl Error for ParseApiVersionError {}
804
805#[doc(hidden)]
806impl Deref for ReadOnlyInstance {
807 type Target = ash::Instance;
808
809 fn deref(&self) -> &Self::Target {
810 &self.inner.instance
811 }
812}
813
814#[cfg(test)]
815mod test {
816 use super::*;
817
818 #[test]
819 pub fn api_versions_match() {
820 assert_eq!(
821 ApiVersion::Vulkan12.to_vk_api_version(),
822 vk::API_VERSION_1_2
823 );
824 assert_eq!(
825 ApiVersion::Vulkan13.to_vk_api_version(),
826 vk::API_VERSION_1_3
827 );
828 }
829
830 #[test]
831 pub fn api_versions_from() {
832 assert_eq!(
833 ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_2).unwrap(),
834 ApiVersion::Vulkan12
835 );
836 assert_eq!(
837 ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_3).unwrap(),
838 ApiVersion::Vulkan13
839 );
840 }
841}