Skip to main content

vk_graph/driver/
instance.rs

1//! Vulkan initialization types
2
3use {
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    // HACK: This is not production-quality
70    // TODO: This was debugged and the issue has not been found, so this may or may not be valid
71    // The validation layer reports `UNASSIGNED-Threading-MultipleThreads-Write` when two threads
72    // touch different VkQueue handles at the same time. Ignoring until the issue is found.
73    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
154// Copied from ash_window::enumerate_required_extensions to change the signature.
155fn 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
177// Estimates surface extension support.
178//
179// Imported instances do not expose their enabled extension list, so we infer support by
180// checking that the VK_KHR_surface entry points resolve for this instance handle.
181fn 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/// Vulkan API version.
198#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
199pub enum ApiVersion {
200    /// Version `1.2`.
201    #[default]
202    Vulkan12,
203
204    /// Version `1.3`.
205    Vulkan13,
206}
207
208impl ApiVersion {
209    /// Returns a version parsed from a native Vulkan value.
210    pub fn try_parse_vk_api_version(version: u32) -> Result<Self, ParseApiVersionError> {
211        Self::try_from(version)
212    }
213
214    /// Vulkan API major version number component. Ex: `vX.0.0-0`.
215    ///
216    /// Always one.
217    pub fn major(self) -> u32 {
218        1
219    }
220
221    /// Vulkan API minor version number component. Ex: `v0.X.0-0`.
222    pub fn minor(self) -> u32 {
223        match self {
224            Self::Vulkan12 => 2,
225            Self::Vulkan13 => 3,
226        }
227    }
228
229    /// Vulkan API minor version number component. Ex: `v0.0.X-0`.
230    ///
231    /// Always zero.
232    pub fn patch(self) -> u32 {
233        0
234    }
235
236    /// Returns a native Vulkan value.
237    pub fn to_vk_api_version(self) -> u32 {
238        self.into()
239    }
240
241    /// Vulkan API variant version number component. Ex: `v0.0.0-X`.
242    ///
243    /// Always zero.
244    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/// There is no global state in Vulkan and all per-application state is stored in a VkInstance
281/// object.
282///
283/// Creating an Instance initializes the Vulkan library and allows the application to pass
284/// information about itself to the implementation.
285#[read_only::embed]
286#[allow(private_interfaces)]
287pub struct Instance {
288    /// Information used to create this resource.
289    ///
290    /// _Note:_ This field is read-only.
291    #[readonly]
292    pub info: InstanceInfo,
293
294    #[readonly]
295    pub(self) inner: Arc<InstanceInner>,
296
297    /// True if `VK_KHR_surface` is enabled on this instance.
298    ///
299    /// _Note:_ This field is read-only.
300    #[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    /// The most recent supported version of Vulkan.
318    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    /// Creates a new Vulkan instance.
327    ///
328    /// This constructor is intended for headless or manually managed setups. It does not infer or
329    /// enable display platform surface extensions. Use [`Self::try_from_display`] when the
330    /// resulting instance must be capable of later surface creation.
331    #[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        // Required to enable non-uniform descriptor indexing (bindless)
341        #[cfg(target_os = "macos")]
342        unsafe {
343            set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1");
344        }
345
346        // Link the Vulkan loader dynamically (default feature).
347        #[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        // Link the Vulkan loader statically if explicitly requested
357        #[cfg(not(feature = "loaded"))]
358        let entry = {
359            #[cfg(not(target_os = "macos"))]
360            let entry = ash::Entry::linked();
361
362            // On MacOS, by default link molten-vk statically using ash-molten.
363            #[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        // If linking dynamically on MacOS, we require a few additional extensions.
379        // Based on "Encountered VK_ERROR_INCOMPATIBLE_DRIVER" section in:
380        // https://vulkan.lunarg.com/doc/view/latest/mac/getting_started.html
381        #[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        // Molten-vk doesn't support the full Vulkan feature set, hence the portability flag needs
417        // to be set.
418        #[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    /// The ash entrypoint used to load Vulkan instance functions.
509    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    /// Returns a wrapper structure for a physical device of this instance.
520    #[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    /// Returns the available physical devices of this instance.
541    #[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    /// Creates a new Vulkan instance with the platform surface extensions required by the provided
588    /// display handle.
589    #[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    /// Loads an existing Vulkan instance that may have been created by other means.
610    ///
611    /// This is useful when you want to use a Vulkan instance created by some other library, such
612    /// as OpenXR.
613    #[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                // The implementation *should* provide a version. If it does not we just send it.
633                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/// Information used to create an [`Instance`] instance.
670#[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    /// The Vulkan API version to target.
678    #[builder(default = "ApiVersion::Vulkan13")]
679    pub api_version: ApiVersion,
680
681    /// Enables Vulkan validation layers.
682    ///
683    /// This requires a Vulkan SDK installation and will cause validation errors to introduce
684    /// panics as they happen.
685    ///
686    /// Set `VK_GRAPH_SKIP_VALIDATION_PARK=1` to keep logging validation errors without parking the
687    /// callback thread for debugger attach.
688    ///
689    /// _NOTE:_ Consider turning OFF debug if you discover an unknown issue. Often the validation
690    /// layers will throw an error before other layers can provide additional context such as the
691    /// API dump info or other messages. You might find the "actual" issue is detailed in those
692    /// subsequent details.
693    ///
694    /// ## Platform-specific
695    ///
696    /// **macOS:** Has no effect unless the `loaded` feature is enabled.
697    #[builder(default)]
698    pub debug: bool,
699
700    /// Required Vulkan instance extension names to load.
701    #[builder(default)]
702    pub extension_names: &'static [&'static CStr],
703}
704
705impl InstanceInfo {
706    /// Creates a default `InstanceInfoBuilder`.
707    pub fn builder() -> InstanceInfoBuilder {
708        Default::default()
709    }
710
711    /// Converts a `InstanceInfo` into a `InstanceInfoBuilder`.
712    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    /// Builds a new `InstanceInfo`.
729    #[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/// Data returned when attempting to parse a Vulkan API version number.
775#[derive(Clone, Copy, Debug)]
776pub struct ParseApiVersionError {
777    /// The _major_ version indicates a significant change in the API, which will encompass a
778    /// wholly new version of the specification.
779    pub major: u32,
780
781    /// The _minor_ version indicates the incorporation of new functionality into the core
782    /// specification.
783    pub minor: u32,
784
785    /// The _patch_ version indicates bug fixes, clarifications, and language improvements have
786    /// been incorporated into the specification.
787    pub patch: u32,
788
789    /// The _variant_ indicates the variant of the Vulkan API supported by the implementation. This
790    /// is always 0 for the Vulkan API.
791    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}