Skip to main content

vk_graph/driver/
instance.rs

1//! Vulkan initialization types.
2
3use {
4    super::{DriverError, physical_device::PhysicalDevice},
5    ash::{
6        ext, khr,
7        vk::{self, Handle},
8    },
9    derive_builder::Builder,
10    log::{debug, error, trace, warn},
11    raw_window_handle::{HasDisplayHandle, RawDisplayHandle},
12    std::{
13        collections::HashSet,
14        error::Error,
15        ffi::CStr,
16        fmt::{Debug, Display, Formatter},
17        ops::Deref,
18        sync::Arc,
19        thread::panicking,
20    },
21};
22
23#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
24use {
25    log::{Level, Metadata, info, logger},
26    std::{
27        env::var,
28        ffi::c_void,
29        io::{IsTerminal, stderr},
30        process::{abort, id},
31        thread::{current, park},
32    },
33};
34
35#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
36const SKIP_VALIDATION_PARK_ENV: &str = "VK_GRAPH_SKIP_VALIDATION_PARK";
37
38#[cfg(target_os = "macos")]
39use std::env::set_var;
40
41#[cfg(any(not(target_os = "macos"), feature = "loaded"))]
42unsafe extern "system" fn debug_callback(
43    message_severity: vk::DebugUtilsMessageSeverityFlagsEXT,
44    _message_types: vk::DebugUtilsMessageTypeFlagsEXT,
45    callback_data: *const vk::DebugUtilsMessengerCallbackDataEXT<'_>,
46    _user_data: *mut c_void,
47) -> vk::Bool32 {
48    if panicking() {
49        return vk::FALSE;
50    }
51
52    assert!(!callback_data.is_null());
53
54    let callback_data = unsafe { &*callback_data };
55    let message = if callback_data.p_message.is_null() {
56        "<missing Vulkan validation message>"
57    } else {
58        unsafe { CStr::from_ptr(callback_data.p_message) }
59            .to_str()
60            .unwrap_or("<invalid Vulkan validation message>")
61    };
62
63    if !callback_data.p_message_id_name.is_null() {
64        let vuid = unsafe { CStr::from_ptr(callback_data.p_message_id_name) }
65            .to_str()
66            .unwrap_or("<invalid Vulkan validation message ID name>");
67        if vuid != "Loader Message" {
68            debug!("{vuid}");
69        }
70    };
71
72    let is_error = message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::ERROR);
73
74    if is_error {
75        error!("{message}");
76    } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::WARNING) {
77        warn!("{message}");
78    } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::INFO) {
79        info!("{message}");
80    } else if message_severity.contains(vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE) {
81        debug!("{message}");
82    }
83
84    if !is_error {
85        return vk::FALSE;
86    }
87
88    if !logger().enabled(&Metadata::builder().level(Level::Debug).build())
89        || var("RUST_LOG")
90            .map(|rust_log| rust_log.is_empty())
91            .unwrap_or(true)
92    {
93        eprintln!(
94            "note: run with `RUST_LOG=trace` environment variable to display more information"
95        );
96        eprintln!("note: see https://github.com/rust-lang/log#in-executables");
97        abort()
98    }
99
100    if current().name() != Some("main") {
101        warn!("invalid validation callback thread: child thread")
102    }
103
104    if var(SKIP_VALIDATION_PARK_ENV)
105        .map(|value| !matches!(value.as_str(), "" | "0" | "false" | "False" | "FALSE"))
106        .unwrap_or(false)
107    {
108        warn!("validation callback park skipped; execution will continue");
109
110        return vk::FALSE;
111    }
112
113    if !stderr().is_terminal() {
114        warn!("validation callback park skipped; stderr is not an interactive terminal");
115
116        return vk::FALSE;
117    }
118
119    debug!(
120        "parking validation callback thread `{}` for debugger attach to pid {}",
121        current().name().unwrap_or_default(),
122        id()
123    );
124
125    logger().flush();
126    park();
127
128    vk::FALSE
129}
130
131fn debug_extension_names() -> &'static [&'static CStr] {
132    #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
133    return &[ext::debug_utils::NAME];
134
135    #[cfg(all(target_os = "macos", not(feature = "loaded")))]
136    return &[];
137}
138
139fn debug_layer_names() -> &'static [&'static CStr] {
140    #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
141    return &[c"VK_LAYER_KHRONOS_validation"];
142
143    #[cfg(all(target_os = "macos", not(feature = "loaded")))]
144    return &[];
145}
146
147// Copied from ash_window::enumerate_required_extensions to change the signature
148fn display_extension_names(
149    display_handle: RawDisplayHandle,
150) -> Result<&'static [&'static CStr], DriverError> {
151    let extensions = match display_handle {
152        RawDisplayHandle::Windows(_) => &[khr::surface::NAME, khr::win32_surface::NAME],
153        RawDisplayHandle::Wayland(_) => &[khr::surface::NAME, khr::wayland_surface::NAME],
154        RawDisplayHandle::Xlib(_) => &[khr::surface::NAME, khr::xlib_surface::NAME],
155        RawDisplayHandle::Xcb(_) => &[khr::surface::NAME, khr::xcb_surface::NAME],
156        RawDisplayHandle::Android(_) => &[khr::surface::NAME, khr::android_surface::NAME],
157        RawDisplayHandle::AppKit(_) | RawDisplayHandle::UiKit(_) => {
158            &[khr::surface::NAME, ext::metal_surface::NAME]
159        }
160        _ => {
161            warn!("unsupported display handle type: {display_handle:?}");
162
163            return Err(DriverError::Unsupported);
164        }
165    };
166
167    Ok(extensions)
168}
169
170/*
171Estimates surface extension support.
172
173Imported instances do not expose their enabled extension list, so we infer support by checking that
174the VK_KHR_surface entry points resolve for this instance handle.
175*/
176fn has_vk_khr_surface(entry: &ash::Entry, instance: vk::Instance) -> bool {
177    [
178        c"vkGetPhysicalDeviceSurfaceCapabilitiesKHR",
179        c"vkGetPhysicalDeviceSurfaceFormatsKHR",
180        c"vkGetPhysicalDeviceSurfacePresentModesKHR",
181        c"vkGetPhysicalDeviceSurfaceSupportKHR",
182        c"vkDestroySurfaceKHR",
183    ]
184    .into_iter()
185    .all(|name| unsafe {
186        entry
187            .get_instance_proc_addr(instance, name.as_ptr())
188            .is_some()
189    })
190}
191
192/// Vulkan API version.
193///
194/// See [`VkApplicationInfo::apiVersion`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkApplicationInfo.html).
195#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord)]
196pub enum ApiVersion {
197    /// Version `1.2`.
198    Vulkan12,
199
200    /// Version `1.3`.
201    ///
202    /// This is the default value.
203    #[default]
204    Vulkan13,
205}
206
207impl ApiVersion {
208    /// Returns a version parsed from a native Vulkan value.
209    pub fn try_parse_vk_api_version(version: u32) -> Result<Self, ParseApiVersionError> {
210        Self::try_from(version)
211    }
212
213    /// Vulkan API major version number component. Ex: `vX.0.0-0`.
214    ///
215    /// Always one.
216    pub fn major(self) -> u32 {
217        1
218    }
219
220    /// Vulkan API minor version number component. Ex: `v0.X.0-0`.
221    pub fn minor(self) -> u32 {
222        match self {
223            Self::Vulkan12 => 2,
224            Self::Vulkan13 => 3,
225        }
226    }
227
228    /// Vulkan API patch version number component. Ex: `v0.0.X-0`.
229    ///
230    /// Always zero.
231    pub fn patch(self) -> u32 {
232        0
233    }
234
235    /// Returns a native Vulkan value.
236    pub fn to_vk_api_version(self) -> u32 {
237        self.into()
238    }
239
240    /// Vulkan API variant version number component. Ex: `v0.0.0-X`.
241    ///
242    /// Always zero.
243    pub fn variant(self) -> u32 {
244        0
245    }
246}
247
248impl From<ApiVersion> for u32 {
249    fn from(val: ApiVersion) -> Self {
250        vk::make_api_version(val.variant(), val.major(), val.minor(), val.patch())
251    }
252}
253
254impl TryFrom<u32> for ApiVersion {
255    type Error = ParseApiVersionError;
256
257    fn try_from(val: u32) -> Result<Self, Self::Error> {
258        let major = vk::api_version_major(val);
259        let minor = vk::api_version_minor(val);
260        let patch = vk::api_version_patch(val);
261        let variant = vk::api_version_variant(val);
262
263        if variant != 0 || major != 1 || minor < 2 {
264            return Err(ParseApiVersionError {
265                major,
266                minor,
267                patch,
268                variant,
269            });
270        }
271
272        Ok(match minor {
273            2 => ApiVersion::Vulkan12,
274            _ => ApiVersion::Vulkan13,
275        })
276    }
277}
278
279/// There is no global state in Vulkan and all per-application state is stored in a `VkInstance`
280/// object.
281///
282/// Creating an `Instance` initializes the Vulkan library and allows the application to pass
283/// information about itself to the implementation.
284///
285/// See [`VkInstance`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkInstance.html).
286#[read_only::embed]
287#[allow(private_interfaces)]
288pub struct Instance {
289    /// Information used to create this resource.
290    ///
291    /// _Note:_ This field is read-only.
292    #[readonly]
293    pub info: InstanceInfo,
294
295    #[readonly]
296    pub(self) inner: Arc<InstanceInner>,
297
298    /// True if `VK_KHR_surface` is enabled on this instance.
299    ///
300    /// _Note:_ This field is read-only.
301    #[readonly]
302    pub khr_surface: bool,
303}
304
305impl Clone for Instance {
306    fn clone(&self) -> Self {
307        Self {
308            read_only: ReadOnlyInstance {
309                info: self.info,
310                inner: self.inner.clone(),
311                khr_surface: self.khr_surface,
312            },
313        }
314    }
315}
316
317impl Instance {
318    /// Default Vulkan API version requested when creating an instance.
319    pub const DEFAULT_API_VERSION: ApiVersion = ApiVersion::Vulkan13;
320
321    /// Creates a new Vulkan instance.
322    ///
323    /// This constructor is intended for headless or manually managed setups. It does not infer or
324    /// enable display platform surface extensions. Use [`Self::try_from_display`] when the
325    /// resulting instance must be capable of later surface creation.
326    ///
327    /// See [`vkCreateInstance`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCreateInstance.html).
328    #[profiling::function]
329    pub fn create(info: impl Into<InstanceInfo>) -> Result<Self, DriverError> {
330        Self::create_with_extension_names(info.into(), &[])
331    }
332
333    fn create_with_extension_names(
334        info: InstanceInfo,
335        extra_extension_names: &[&CStr],
336    ) -> Result<Self, DriverError> {
337        if info.debug && debug_extension_names().is_empty() {
338            error!("debug mode requires VK_EXT_debug_utils support");
339
340            return Err(DriverError::Unsupported);
341        }
342
343        // Required to enable non-uniform descriptor indexing (bindless)
344        #[cfg(target_os = "macos")]
345        unsafe {
346            set_var("MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS", "1");
347        }
348
349        // Link the Vulkan loader dynamically (default feature)
350        #[cfg(feature = "loaded")]
351        let entry = unsafe {
352            ash::Entry::load().map_err(|err| {
353                error!("unable to load Vulkan driver: {err}");
354
355                DriverError::Unsupported
356            })?
357        };
358
359        // Link the Vulkan loader statically if explicitly requested
360        #[cfg(not(feature = "loaded"))]
361        let entry = {
362            #[cfg(not(target_os = "macos"))]
363            let entry = ash::Entry::linked();
364
365            // On macOS, by default link molten-vk statically using ash-molten
366            #[cfg(target_os = "macos")]
367            let entry = ash_molten::load();
368        };
369
370        let mut extension_names = info
371            .extension_names
372            .iter()
373            .chain(extra_extension_names)
374            .copied()
375            .collect::<HashSet<_>>();
376
377        if info.debug {
378            extension_names.extend(debug_extension_names());
379        }
380
381        /*
382        If linking dynamically on macOS, we require a few additional extensions. Based on
383        "Encountered VK_ERROR_INCOMPATIBLE_DRIVER" section in:
384        https://vulkan.lunarg.com/doc/view/latest/mac/getting_started.html
385        */
386        #[cfg(all(target_os = "macos", feature = "loaded"))]
387        {
388            extension_names.extend(&[
389                ash::khr::get_physical_device_properties2::NAME,
390                ash::khr::portability_enumeration::NAME,
391            ]);
392        }
393
394        let khr_surface = extension_names.contains(&khr::surface::NAME);
395
396        let extension_name_ptrs = extension_names
397            .iter()
398            .copied()
399            .map(CStr::as_ptr)
400            .collect::<Box<_>>();
401
402        let mut layer_names = Vec::with_capacity(info.debug as _);
403
404        if info.debug {
405            layer_names.extend(debug_layer_names());
406        }
407
408        let layer_name_ptrs = layer_names
409            .iter()
410            .copied()
411            .map(CStr::as_ptr)
412            .collect::<Box<_>>();
413
414        let app_desc =
415            vk::ApplicationInfo::default().api_version(info.api_version.to_vk_api_version());
416        let instance_desc = vk::InstanceCreateInfo::default()
417            .application_info(&app_desc)
418            .enabled_layer_names(&layer_name_ptrs)
419            .enabled_extension_names(&extension_name_ptrs);
420
421        /*
422        MoltenVK doesn't support the full Vulkan feature set, hence the portability flag needs to be
423        set.
424        */
425        #[cfg(all(target_os = "macos", feature = "loaded"))]
426        let instance_desc = instance_desc.flags(vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR);
427
428        #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
429        let mut debug_create_info = vk::DebugUtilsMessengerCreateInfoEXT::default()
430            .message_severity(
431                vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE
432                    | vk::DebugUtilsMessageSeverityFlagsEXT::INFO
433                    | vk::DebugUtilsMessageSeverityFlagsEXT::WARNING
434                    | vk::DebugUtilsMessageSeverityFlagsEXT::ERROR,
435            )
436            .message_type(
437                vk::DebugUtilsMessageTypeFlagsEXT::GENERAL
438                    | vk::DebugUtilsMessageTypeFlagsEXT::VALIDATION
439                    | vk::DebugUtilsMessageTypeFlagsEXT::PERFORMANCE,
440            )
441            .pfn_user_callback(Some(debug_callback));
442
443        #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
444        let instance_desc = if info.debug {
445            instance_desc.push_next(&mut debug_create_info)
446        } else {
447            instance_desc
448        };
449
450        let instance = unsafe {
451            entry.create_instance(&instance_desc, None).map_err(|_| {
452                if info.debug {
453                    warn!("debug may only be enabled with a valid Vulkan SDK installation");
454                }
455
456                error!(
457                    "Vulkan driver does not support API v{}",
458                    match info.api_version {
459                        ApiVersion::Vulkan12 => "1.2",
460                        ApiVersion::Vulkan13 => "1.3",
461                    }
462                );
463
464                for layer_name in &layer_names {
465                    debug!("Layer: {:?}", layer_name);
466                }
467
468                for extension_name in extension_names {
469                    debug!("Extension: {:?}", extension_name);
470                }
471
472                DriverError::Unsupported
473            })?
474        };
475
476        trace!("created a Vulkan instance");
477
478        #[cfg(all(target_os = "macos", not(feature = "loaded")))]
479        let debug_utils = None;
480
481        #[cfg(any(not(target_os = "macos"), feature = "loaded"))]
482        let debug_utils = if info.debug {
483            let debug_utils = ext::debug_utils::Instance::new(&entry, &instance);
484            let debug_messenger =
485                unsafe { debug_utils.create_debug_utils_messenger(&debug_create_info, None) }
486                    .map_err(|err| {
487                        unsafe {
488                            instance.destroy_instance(None);
489                        }
490
491                        error!("unable to create debug utils messenger: {err}");
492
493                        DriverError::Unsupported
494                    })?;
495
496            Some((debug_utils, debug_messenger))
497        } else {
498            None
499        };
500
501        Ok(Self {
502            read_only: ReadOnlyInstance {
503                info,
504                inner: Arc::new(InstanceInner {
505                    debug_utils,
506                    entry,
507                    instance,
508                    instance_created: true,
509                }),
510                khr_surface,
511            },
512        })
513    }
514
515    /// The ash entry point used to load Vulkan instance functions.
516    pub fn entry(this: &Self) -> &ash::Entry {
517        &this.inner.entry
518    }
519
520    pub(crate) fn supports_debug_utils(this: &Self) -> bool {
521        this.inner.debug_utils.is_some()
522    }
523
524    /// Returns the available physical devices of this instance.
525    ///
526    /// See [`vkEnumeratePhysicalDevices`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkEnumeratePhysicalDevices.html).
527    #[profiling::function]
528    pub fn physical_devices(
529        this: &Self,
530    ) -> Result<impl IntoIterator<Item = PhysicalDevice>, DriverError> {
531        let physical_devices = unsafe { this.enumerate_physical_devices() }.map_err(|err| {
532            error!("unable to enumerate physical devices: {err}");
533
534            match err {
535                vk::Result::ERROR_INITIALIZATION_FAILED => DriverError::Unsupported,
536                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
537                    DriverError::OutOfMemory
538                }
539                vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
540                _ => {
541                    warn!("unexpected enumerate_physical_devices error: {err}");
542
543                    DriverError::Unsupported
544                }
545            }
546        })?;
547
548        Ok(physical_devices
549            .into_iter()
550            .enumerate()
551            .filter_map(|(idx, physical_device)| {
552                let physical_device = unsafe {
553                    PhysicalDevice::try_from_ash(this, physical_device)
554                        .inspect_err(|err| warn!("unsupported physical device #{idx}: {err}"))
555                        .ok()?
556                };
557
558                let api_version = ApiVersion::try_parse_vk_api_version(
559                    physical_device.properties_v1_0.api_version,
560                )
561                .inspect_err(|err| {
562                    warn!(
563                        "unsupported physical device #{idx} {}: {err}",
564                        physical_device.properties_v1_0.device_name
565                    );
566                })
567                .ok()?;
568
569                if api_version < this.info.api_version {
570                    return None;
571                }
572
573                if this.info.debug && !physical_device.vk_ext_private_data {
574                    warn!(
575                        "unsupported physical device #{idx} {}: missing VK_EXT_private_data",
576                        physical_device.properties_v1_0.device_name
577                    );
578
579                    return None;
580                }
581
582                Some(physical_device)
583            }))
584    }
585
586    /// Creates a new Vulkan instance with the platform surface extensions required by the provided
587    /// display handle.
588    ///
589    /// See [`VK_KHR_surface`](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_surface.html).
590    #[profiling::function]
591    pub fn try_from_display(
592        display: impl HasDisplayHandle,
593        info: impl Into<InstanceInfo>,
594    ) -> Result<Self, DriverError> {
595        let display_handle = display.display_handle().map_err(|err| {
596            warn!("unable to get display handle: {err}");
597
598            DriverError::Unsupported
599        })?;
600        let display_extension_names =
601            display_extension_names(display_handle.as_raw()).map_err(|err| {
602                warn!("unable to enumerate display extensions: {err}");
603
604                DriverError::Unsupported
605            })?;
606
607        Self::create_with_extension_names(info.into(), display_extension_names)
608    }
609
610    /// Loads an existing Vulkan instance that may have been created by other means.
611    ///
612    /// This is useful when you want to use a Vulkan instance created by some other library, such
613    /// as OpenXR.
614    ///
615    /// See [`VkInstance`](https://registry.khronos.org/vulkan/specs/latest/man/html/VkInstance.html).
616    #[profiling::function]
617    pub fn try_from_entry(entry: ash::Entry, instance: vk::Instance) -> Result<Self, DriverError> {
618        if instance == vk::Instance::null() {
619            warn!("invalid VkInstance handle: null");
620
621            return Err(DriverError::InvalidData);
622        }
623
624        let api_version = unsafe { entry.try_enumerate_instance_version() }
625            .map_err(|err| match err {
626                vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
627                vk::Result::ERROR_VALIDATION_FAILED_EXT => DriverError::InvalidData,
628                err => {
629                    error!("unable to enumerate instance version: {err}");
630
631                    DriverError::Unsupported
632                }
633            })?
634            .unwrap_or_else(|| {
635                /*
636                The implementation *should* provide a version. If it does not, use the default.
637                */
638                Self::DEFAULT_API_VERSION.to_vk_api_version()
639            })
640            .try_into()
641            .map_err(|err| {
642                warn!("unsupported instance: {err}");
643
644                DriverError::Unsupported
645            })?;
646        let khr_surface = has_vk_khr_surface(&entry, instance);
647
648        let instance = unsafe { ash::Instance::load(entry.static_fn(), instance) };
649
650        Ok(Self {
651            read_only: ReadOnlyInstance {
652                info: InstanceInfo {
653                    api_version,
654                    ..Default::default()
655                },
656                inner: Arc::new(InstanceInner {
657                    debug_utils: None,
658                    entry,
659                    instance,
660                    instance_created: false,
661                }),
662                khr_surface,
663            },
664        })
665    }
666}
667
668impl Debug for Instance {
669    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
670        f.debug_struct(stringify!(Instance))
671            .field("handle", &self.inner.instance.handle())
672            .field("info", &self.info)
673            .field("khr_surface", &self.khr_surface)
674            .field("debug_utils", &self.inner.debug_utils.is_some())
675            .finish_non_exhaustive()
676    }
677}
678
679/// Information used to create an [`Instance`] instance.
680#[derive(Builder, Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
681#[builder(
682    build_fn(private, name = "fallible_build"),
683    derive(Clone, Copy, Debug),
684    pattern = "owned"
685)]
686pub struct InstanceInfo {
687    /// The Vulkan API version to target.
688    ///
689    /// Defaults to [`Instance::DEFAULT_API_VERSION`].
690    #[builder(default = "Instance::DEFAULT_API_VERSION")]
691    pub api_version: ApiVersion,
692
693    /// Enables Vulkan validation layers.
694    ///
695    /// This requires a Vulkan SDK installation and will panic when validation errors happen.
696    /// Additionally, the device must support VK_EXT_private_data.
697    ///
698    /// When `stderr` is attached to an interactive terminal, validation errors will park the
699    /// callback thread for debugger attach.
700    ///
701    /// Set `VK_GRAPH_SKIP_VALIDATION_PARK=1` to keep logging validation errors without parking.
702    ///
703    /// _NOTE:_ Consider turning OFF debug if you discover an unknown issue. Often the validation
704    /// layers will report an error before other layers can provide additional context such as the
705    /// API dump info or other messages. You might find the "actual" issue is detailed in those
706    /// subsequent details.
707    ///
708    /// ## Platform-specific
709    ///
710    /// **macOS:** Has no effect unless the `loaded` feature is enabled.
711    ///
712    #[builder(default)]
713    pub debug: bool,
714
715    /// Required Vulkan instance extension names to load.
716    #[builder(default)]
717    pub extension_names: &'static [&'static CStr],
718}
719
720impl InstanceInfo {
721    /// Creates a default `InstanceInfoBuilder`.
722    pub fn builder() -> InstanceInfoBuilder {
723        Default::default()
724    }
725
726    /// Converts a `InstanceInfo` into a `InstanceInfoBuilder`.
727    pub fn into_builder(self) -> InstanceInfoBuilder {
728        InstanceInfoBuilder {
729            api_version: Some(self.api_version),
730            debug: Some(self.debug),
731            extension_names: Some(self.extension_names),
732        }
733    }
734}
735
736impl InstanceInfoBuilder {
737    /// Builds a new `InstanceInfo`.
738    #[inline(always)]
739    pub fn build(self) -> InstanceInfo {
740        self.fallible_build().expect("invalid instance info")
741    }
742}
743
744impl From<InstanceInfoBuilder> for InstanceInfo {
745    fn from(info: InstanceInfoBuilder) -> Self {
746        info.build()
747    }
748}
749
750struct InstanceInner {
751    debug_utils: Option<(ext::debug_utils::Instance, vk::DebugUtilsMessengerEXT)>,
752    entry: ash::Entry,
753    instance: ash::Instance,
754    instance_created: bool,
755}
756
757impl Drop for InstanceInner {
758    #[profiling::function]
759    fn drop(&mut self) {
760        if panicking() {
761            return;
762        }
763
764        unsafe {
765            if let Some((debug_utils, debug_messenger)) = self.debug_utils.take() {
766                trace!("destroy debug_utils_messenger {}", debug_messenger.as_raw());
767                debug_utils.destroy_debug_utils_messenger(debug_messenger, None);
768                trace!(
769                    "destroy debug_utils_messenger {} DONE",
770                    debug_messenger.as_raw()
771                );
772            }
773
774            if self.instance_created {
775                trace!("destroy instance {}", self.instance.handle().as_raw());
776                self.instance.destroy_instance(None);
777                self.instance_created = false;
778            }
779        }
780    }
781}
782
783/// Data returned when attempting to parse a Vulkan API version number.
784#[derive(Clone, Copy, Debug)]
785pub struct ParseApiVersionError {
786    /// The _major_ version indicates a significant change in the API, which will encompass a
787    /// wholly new version of the specification.
788    pub major: u32,
789
790    /// The _minor_ version indicates the incorporation of new functionality into the core
791    /// specification.
792    pub minor: u32,
793
794    /// The _patch_ version indicates bug fixes, clarifications, and language improvements have
795    /// been incorporated into the specification.
796    pub patch: u32,
797
798    /// The _variant_ indicates the variant of the Vulkan API supported by the implementation. This
799    /// is always 0 for the Vulkan API.
800    pub variant: u32,
801}
802
803impl Display for ParseApiVersionError {
804    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
805        f.write_fmt(format_args!(
806            "v{}.{}.{}-{}",
807            self.major, self.minor, self.patch, self.variant
808        ))
809    }
810}
811
812impl Error for ParseApiVersionError {}
813
814#[doc(hidden)]
815impl Deref for ReadOnlyInstance {
816    type Target = ash::Instance;
817
818    fn deref(&self) -> &Self::Target {
819        &self.inner.instance
820    }
821}
822
823#[cfg(test)]
824mod test {
825    use super::*;
826
827    #[test]
828    pub fn api_versions_match() {
829        assert_eq!(
830            ApiVersion::Vulkan12.to_vk_api_version(),
831            vk::API_VERSION_1_2
832        );
833        assert_eq!(
834            ApiVersion::Vulkan13.to_vk_api_version(),
835            vk::API_VERSION_1_3
836        );
837    }
838
839    #[test]
840    pub fn api_versions_from() {
841        assert_eq!(
842            ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_2).unwrap(),
843            ApiVersion::Vulkan12
844        );
845        assert_eq!(
846            ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_3).unwrap(),
847            ApiVersion::Vulkan13
848        );
849    }
850
851    #[test]
852    pub fn default_api_version_matches_instance_info_default() {
853        assert_eq!(
854            Instance::DEFAULT_API_VERSION,
855            InstanceInfo::default().api_version
856        );
857    }
858
859    #[test]
860    pub fn default_api_version_matches_api_version_default() {
861        assert_eq!(Instance::DEFAULT_API_VERSION, ApiVersion::default());
862    }
863
864    #[test]
865    pub fn invalid_api_versions_are_rejected() {
866        assert!(ApiVersion::try_parse_vk_api_version(vk::API_VERSION_1_1).is_err());
867        assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(0, 2, 0, 0)).is_err());
868        assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 1, 9, 0)).is_err());
869        assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 4, 2, 0)).is_err());
870        assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 2, 0, 1)).is_err());
871        assert!(ApiVersion::try_parse_vk_api_version(vk::make_api_version(1, 3, 0, 1)).is_err());
872    }
873}