// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

//! API entry point.
//!
//! The first thing to do after loading the Vulkan library is to create an `Instance` object.
//!
//! For example:
//!
//! ```no_run
//! use vulkano::{
//!     instance::{Instance, InstanceExtensions},
//!     Version, VulkanLibrary,
//! };
//!
//! let library = VulkanLibrary::new()
//!     .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err));
//! let instance = Instance::new(library, Default::default())
//!     .unwrap_or_else(|err| panic!("Couldn't create instance: {:?}", err));
//! ```
//!
//! Creating an instance initializes everything and allows you to enumerate physical devices,
//! ie. all the Vulkan implementations that are available on the system.
//!
//! ```no_run
//! # use vulkano::{
//! #     instance::{Instance, InstanceExtensions},
//! #     Version, VulkanLibrary,
//! # };
//! use vulkano::device::physical::PhysicalDevice;
//!
//! # let library = VulkanLibrary::new().unwrap();
//! # let instance = Instance::new(library, Default::default()).unwrap();
//! for physical_device in instance.enumerate_physical_devices().unwrap() {
//!     println!("Available device: {}", physical_device.properties().device_name);
//! }
//! ```
//!
//! # Enumerating physical devices and creating a device
//!
//! After you have created an instance, the next step is usually to enumerate the physical devices
//! that are available on the system with `Instance::enumerate_physical_devices()` (see above).
//!
//! When choosing which physical device to use, keep in mind that physical devices may or may not
//! be able to draw to a certain surface (ie. to a window or a monitor), or may even not be able
//! to draw at all. See the `swapchain` module for more information about surfaces.
//!
//! Once you have chosen a physical device, you can create a `Device` object from it. See the
//! `device` module for more info.
//!
//! # Portability subset devices and the `enumerate_portability` flag
//!
//! Certain devices, currently those on MacOS and iOS systems, do not fully conform to the Vulkan
//! specification. They are usable as normal devices, but they do not implement everything that
//! is required; some mandatory parts of Vulkan are missing. These are known as
//! "portability subset" devices.
//!
//! A portability subset device will advertise support for the
//! [`khr_portability_subset`](crate::device::DeviceExtensions::khr_portability_subset) device
//! extension. This extension must always be enabled when it is supported, and Vulkano will
//! automatically enable it when creating the device. When it is enabled, some parts of Vulkan that
//! are available in standard Vulkan will not be available by default, but they can be used by
//! enabling corresponding features when creating the device, if the device supports them.
//!
//! Because these devices are non-conformant, Vulkan programs that rely on full compliance may
//! not work (crash or have validation errors) when run on them, if they happen to use a part of
//! Vulkan that is missing from the non-conformant device. Therefore, Vulkan hides them from
//! the user by default when calling `enumerate_physical_devices` on the instance. If there are no
//! conformant devices on the system, `Instance::new` will return an `IncompatibleDriver` error.
//!
//! In order to enumerate portability subset devices, you must set the
//! [`InstanceCreateInfo::enumerate_portability`] flag when creating the instance. However, if you
//! do this, your program must be prepared to handle the non-conformant aspects of these devices,
//! and must enable the appropriate features when creating the `Device` if you intend to use them.

use self::debug::{
    DebugUtilsMessengerCreateInfo, UserCallback, ValidationFeatureDisable, ValidationFeatureEnable,
};
pub use self::{extensions::InstanceExtensions, layers::LayerProperties};
use crate::{
    device::physical::PhysicalDevice, instance::debug::trampoline, macros::impl_id_counter,
    OomError, RequiresOneOf, VulkanError, VulkanLibrary, VulkanObject,
};
pub use crate::{
    extensions::{ExtensionRestriction, ExtensionRestrictionError},
    fns::InstanceFunctions,
    version::Version,
};
use smallvec::SmallVec;
use std::{
    error::Error,
    ffi::{c_void, CString},
    fmt::{Debug, Display, Error as FmtError, Formatter},
    mem::MaybeUninit,
    num::NonZeroU64,
    panic::{RefUnwindSafe, UnwindSafe},
    ptr,
    sync::Arc,
};

pub mod debug;
pub(crate) mod extensions;
mod layers;

/// An instance of a Vulkan context. This is the main object that should be created by an
/// application before everything else.
///
/// # Application and engine info
///
/// When you create an instance, you have the possibility to set information about your application
/// and its engine.
///
/// Providing this information allows for example the driver to let the user configure the driver's
/// behavior for your application alone through a control panel.
///
/// ```no_run
/// # #[macro_use] extern crate vulkano;
/// # fn main() {
/// use vulkano::{
///     instance::{Instance, InstanceCreateInfo, InstanceExtensions},
///     Version, VulkanLibrary,
/// };
///
/// let library = VulkanLibrary::new().unwrap();
/// let _instance = Instance::new(
///     library,
///     InstanceCreateInfo::application_from_cargo_toml(),
/// ).unwrap();
/// # }
/// ```
///
/// # API versions
///
/// Both an `Instance` and a [`Device`](crate::device::Device) have a highest version of the Vulkan
/// API that they support. This places a limit on what Vulkan functions and features are available
/// to use when used on a particular instance or device. It is possible for the instance and the
/// device to support different versions. The supported version for an instance can be queried
/// before creation with
/// [`VulkanLibrary::api_version`](crate::VulkanLibrary::api_version),
/// while for a device it can be retrieved with
/// [`PhysicalDevice::api_version`](crate::device::physical::PhysicalDevice::api_version).
///
/// When creating an `Instance`, you have to specify a maximum API version that you will use.
/// This restricts the API version that is available for the instance and any devices created from
/// it. For example, if both instance and device potentially support Vulkan 1.2, but you specify
/// 1.1 as the maximum API version when creating the `Instance`, then you can only use Vulkan 1.1
/// functions, even though they could theoretically support a higher version. You can think of it
/// as a promise never to use any functionality from a higher version.
///
/// The maximum API version is not a _minimum_, so it is possible to set it to a higher version than
/// what the instance or device inherently support. The final API version that you are able to use
/// on an instance or device is the lower of the supported API version and the chosen maximum API
/// version of the `Instance`.
///
/// Due to a quirk in how the Vulkan 1.0 specification was written, if the instance only
/// supports Vulkan 1.0, then it is not possible to specify a maximum API version higher than 1.0.
/// Trying to create an `Instance` will return an `IncompatibleDriver` error. Consequently, it is
/// not possible to use a higher device API version with an instance that only supports 1.0.
///
/// # Extensions
///
/// When creating an `Instance`, you must provide a list of extensions that must be enabled on the
/// newly-created instance. Trying to enable an extension that is not supported by the system will
/// result in an error.
///
/// Contrary to OpenGL, it is not possible to use the features of an extension if it was not
/// explicitly enabled.
///
/// Extensions are especially important to take into account if you want to render images on the
/// screen, as the only way to do so is to use the `VK_KHR_surface` extension. More information
/// about this in the `swapchain` module.
///
/// For example, here is how we create an instance with the `VK_KHR_surface` and
/// `VK_KHR_android_surface` extensions enabled, which will allow us to render images to an
/// Android screen. You can compile and run this code on any system, but it is highly unlikely to
/// succeed on anything else than an Android-running device.
///
/// ```no_run
/// use vulkano::{
///     instance::{Instance, InstanceCreateInfo, InstanceExtensions},
///     Version, VulkanLibrary,
/// };
///
/// let library = VulkanLibrary::new()
///     .unwrap_or_else(|err| panic!("Couldn't load Vulkan library: {:?}", err));
///
/// let extensions = InstanceExtensions {
///     khr_surface: true,
///     khr_android_surface: true,
///     .. InstanceExtensions::empty()
/// };
///
/// let instance = Instance::new(
///     library,
///     InstanceCreateInfo {
///         enabled_extensions: extensions,
///         ..Default::default()
///     },
/// )
/// .unwrap_or_else(|err| panic!("Couldn't create instance: {:?}", err));
/// ```
///
/// # Layers
///
/// When creating an `Instance`, you have the possibility to pass a list of **layers** that will
/// be activated on the newly-created instance. The list of available layers can be retrieved by
/// calling the [`layer_properties`](crate::VulkanLibrary::layer_properties) method of
/// `VulkanLibrary`.
///
/// A layer is a component that will hook and potentially modify the Vulkan function calls.
/// For example, activating a layer could add a frames-per-second counter on the screen, or it
/// could send information to a debugger that will debug your application.
///
/// > **Note**: From an application's point of view, layers "just exist". In practice, on Windows
/// > and Linux, layers can be installed by third party installers or by package managers and can
/// > also be activated by setting the value of the `VK_INSTANCE_LAYERS` environment variable
/// > before starting the program. See the documentation of the official Vulkan loader for these
/// > platforms.
///
/// > **Note**: In practice, the most common use of layers right now is for debugging purposes.
/// > To do so, you are encouraged to set the `VK_INSTANCE_LAYERS` environment variable on Windows
/// > or Linux instead of modifying the source code of your program. For example:
/// > `export VK_INSTANCE_LAYERS=VK_LAYER_LUNARG_api_dump` on Linux if you installed the Vulkan SDK
/// > will print the list of raw Vulkan function calls.
///
/// ## Examples
///
/// ```
/// # use std::{sync::Arc, error::Error};
/// # use vulkano::{
/// #     instance::{Instance, InstanceCreateInfo, InstanceExtensions},
/// #     Version, VulkanLibrary,
/// # };
/// # fn test() -> Result<Arc<Instance>, Box<dyn Error>> {
/// let library = VulkanLibrary::new()?;
///
/// // For the sake of the example, we activate all the layers that
/// // contain the word "foo" in their description.
/// let layers: Vec<_> = library.layer_properties()?
///     .filter(|l| l.description().contains("foo"))
///     .collect();
///
/// let instance = Instance::new(
///     library,
///     InstanceCreateInfo {
///         enabled_layers: layers.iter().map(|l| l.name().to_owned()).collect(),
///         ..Default::default()
///     },
/// )?;
/// # Ok(instance)
/// # }
/// ```
// TODO: mention that extensions must be supported by layers as well
pub struct Instance {
    handle: ash::vk::Instance,
    fns: InstanceFunctions,
    id: NonZeroU64,

    api_version: Version,
    enabled_extensions: InstanceExtensions,
    enabled_layers: Vec<String>,
    library: Arc<VulkanLibrary>,
    max_api_version: Version,
    _user_callbacks: Vec<Box<UserCallback>>,
}

// TODO: fix the underlying cause instead
impl UnwindSafe for Instance {}
impl RefUnwindSafe for Instance {}

impl Instance {
    /// Creates a new `Instance`.
    ///
    /// # Panics
    ///
    /// - Panics if any version numbers in `create_info` contain a field too large to be converted
    ///   into a Vulkan version number.
    /// - Panics if `create_info.max_api_version` is not at least `V1_0`.
    pub fn new(
        library: Arc<VulkanLibrary>,
        create_info: InstanceCreateInfo,
    ) -> Result<Arc<Instance>, InstanceCreationError> {
        unsafe { Self::with_debug_utils_messengers(library, create_info, []) }
    }

    /// Creates a new `Instance` with debug messengers to use during the creation and destruction
    /// of the instance.
    ///
    /// The debug messengers are not used at any other time,
    /// [`DebugUtilsMessenger`](crate::instance::debug::DebugUtilsMessenger) should be used for
    /// that.
    ///
    /// If `debug_utils_messengers` is not empty, the `ext_debug_utils` extension must be set in
    /// `enabled_extensions`.
    ///
    /// # Panics
    ///
    /// - Panics if the `message_severity` or `message_type` members of any element of
    ///   `debug_utils_messengers` are empty.
    ///
    /// # Safety
    ///
    /// - The `user_callback` of each element of `debug_utils_messengers` must not make any calls
    ///   to the Vulkan API.
    pub unsafe fn with_debug_utils_messengers(
        library: Arc<VulkanLibrary>,
        create_info: InstanceCreateInfo,
        debug_utils_messengers: impl IntoIterator<Item = DebugUtilsMessengerCreateInfo>,
    ) -> Result<Arc<Instance>, InstanceCreationError> {
        let InstanceCreateInfo {
            application_name,
            application_version,
            mut enabled_extensions,
            enabled_layers,
            engine_name,
            engine_version,
            max_api_version,
            enumerate_portability,
            enabled_validation_features,
            disabled_validation_features,
            _ne: _,
        } = create_info;

        let (api_version, max_api_version) = {
            let api_version = library.api_version();
            let max_api_version = if let Some(max_api_version) = max_api_version {
                max_api_version
            } else if api_version < Version::V1_1 {
                api_version
            } else {
                Version::HEADER_VERSION
            };

            (std::cmp::min(max_api_version, api_version), max_api_version)
        };

        // VUID-VkApplicationInfo-apiVersion-04010
        assert!(max_api_version >= Version::V1_0);
        let supported_extensions =
            library.supported_extensions_with_layers(enabled_layers.iter().map(String::as_str))?;
        let mut flags = ash::vk::InstanceCreateFlags::empty();

        if enumerate_portability && supported_extensions.khr_portability_enumeration {
            enabled_extensions.khr_portability_enumeration = true;
            flags |= ash::vk::InstanceCreateFlags::ENUMERATE_PORTABILITY_KHR;
        }

        // Check if the extensions are correct
        enabled_extensions.check_requirements(&supported_extensions, api_version)?;

        // FIXME: check whether each layer is supported
        let enabled_layers_cstr: Vec<CString> = enabled_layers
            .iter()
            .map(|name| CString::new(name.clone()).unwrap())
            .collect();
        let enabled_layers_ptrs = enabled_layers_cstr
            .iter()
            .map(|layer| layer.as_ptr())
            .collect::<SmallVec<[_; 2]>>();

        let enabled_extensions_cstr: Vec<CString> = (&enabled_extensions).into();
        let enabled_extensions_ptrs = enabled_extensions_cstr
            .iter()
            .map(|extension| extension.as_ptr())
            .collect::<SmallVec<[_; 2]>>();

        let application_name_cstr = application_name.map(|name| CString::new(name).unwrap());
        let engine_name_cstr = engine_name.map(|name| CString::new(name).unwrap());
        let application_info = ash::vk::ApplicationInfo {
            p_application_name: application_name_cstr
                .as_ref()
                .map(|s| s.as_ptr())
                .unwrap_or(ptr::null()),
            application_version: application_version
                .try_into()
                .expect("Version out of range"),
            p_engine_name: engine_name_cstr
                .as_ref()
                .map(|s| s.as_ptr())
                .unwrap_or(ptr::null()),
            engine_version: engine_version.try_into().expect("Version out of range"),
            api_version: max_api_version.try_into().expect("Version out of range"),
            ..Default::default()
        };

        let enable_validation_features_vk: SmallVec<[_; 5]> = enabled_validation_features
            .iter()
            .copied()
            .map(Into::into)
            .collect();
        let disable_validation_features_vk: SmallVec<[_; 8]> = disabled_validation_features
            .iter()
            .copied()
            .map(Into::into)
            .collect();

        let mut create_info_vk = ash::vk::InstanceCreateInfo {
            flags,
            p_application_info: &application_info,
            enabled_layer_count: enabled_layers_ptrs.len() as u32,
            pp_enabled_layer_names: enabled_layers_ptrs.as_ptr(),
            enabled_extension_count: enabled_extensions_ptrs.len() as u32,
            pp_enabled_extension_names: enabled_extensions_ptrs.as_ptr(),
            ..Default::default()
        };
        let mut validation_features_vk = None;

        if !enabled_validation_features.is_empty() || !disabled_validation_features.is_empty() {
            if !enabled_extensions.ext_validation_features {
                return Err(InstanceCreationError::RequirementNotMet {
                    required_for: "`create_info.enabled_validation_features` or \
                        `create_info.disabled_validation_features` are not empty",
                    requires_one_of: RequiresOneOf {
                        instance_extensions: &["ext_validation_features"],
                        ..Default::default()
                    },
                });
            }

            // VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02967
            assert!(
                !enabled_validation_features
                    .contains(&ValidationFeatureEnable::GpuAssistedReserveBindingSlot)
                    || enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted)
            );

            // VUID-VkValidationFeaturesEXT-pEnabledValidationFeatures-02968
            assert!(
                !(enabled_validation_features.contains(&ValidationFeatureEnable::DebugPrintf)
                    && enabled_validation_features.contains(&ValidationFeatureEnable::GpuAssisted))
            );

            let next = validation_features_vk.insert(ash::vk::ValidationFeaturesEXT {
                enabled_validation_feature_count: enable_validation_features_vk.len() as u32,
                p_enabled_validation_features: enable_validation_features_vk.as_ptr(),
                disabled_validation_feature_count: disable_validation_features_vk.len() as u32,
                p_disabled_validation_features: disable_validation_features_vk.as_ptr(),
                ..Default::default()
            });

            next.p_next = create_info_vk.p_next;
            create_info_vk.p_next = next as *const _ as *const _;
        }

        // Handle debug messengers
        let debug_utils_messengers = debug_utils_messengers.into_iter();
        let mut debug_utils_messenger_create_infos =
            Vec::with_capacity(debug_utils_messengers.size_hint().0);
        let mut user_callbacks = Vec::with_capacity(debug_utils_messengers.size_hint().0);

        for create_info in debug_utils_messengers {
            let DebugUtilsMessengerCreateInfo {
                message_type,
                message_severity,
                user_callback,
                _ne: _,
            } = create_info;

            // VUID-VkInstanceCreateInfo-pNext-04926
            if !enabled_extensions.ext_debug_utils {
                return Err(InstanceCreationError::RequirementNotMet {
                    required_for: "`create_info.debug_utils_messengers` is not empty",
                    requires_one_of: RequiresOneOf {
                        instance_extensions: &["ext_debug_utils"],
                        ..Default::default()
                    },
                });
            }

            // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-parameter
            // TODO: message_severity.validate_instance()?;

            // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageSeverity-requiredbitmask
            assert!(!message_severity.is_empty());

            // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-parameter
            // TODO: message_type.validate_instance()?;

            // VUID-VkDebugUtilsMessengerCreateInfoEXT-messageType-requiredbitmask
            assert!(!message_type.is_empty());

            // VUID-PFN_vkDebugUtilsMessengerCallbackEXT-None-04769
            // Can't be checked, creation is unsafe.

            let user_callback = Box::new(user_callback);
            let create_info = ash::vk::DebugUtilsMessengerCreateInfoEXT {
                flags: ash::vk::DebugUtilsMessengerCreateFlagsEXT::empty(),
                message_severity: message_severity.into(),
                message_type: message_type.into(),
                pfn_user_callback: Some(trampoline),
                p_user_data: &*user_callback as &Arc<_> as *const Arc<_> as *const c_void as *mut _,
                ..Default::default()
            };

            debug_utils_messenger_create_infos.push(create_info);
            user_callbacks.push(user_callback);
        }

        for i in 1..debug_utils_messenger_create_infos.len() {
            debug_utils_messenger_create_infos[i - 1].p_next =
                &debug_utils_messenger_create_infos[i] as *const _ as *const _;
        }

        if let Some(info) = debug_utils_messenger_create_infos.first() {
            create_info_vk.p_next = info as *const _ as *const _;
        }

        // Creating the Vulkan instance.
        let handle = {
            let mut output = MaybeUninit::uninit();
            let fns = library.fns();
            (fns.v1_0.create_instance)(&create_info_vk, ptr::null(), output.as_mut_ptr())
                .result()
                .map_err(VulkanError::from)?;
            output.assume_init()
        };

        // Loading the function pointers of the newly-created instance.
        let fns = {
            InstanceFunctions::load(|name| {
                library
                    .get_instance_proc_addr(handle, name.as_ptr())
                    .map_or(ptr::null(), |func| func as _)
            })
        };

        Ok(Arc::new(Instance {
            handle,
            fns,
            id: Self::next_id(),
            api_version,
            enabled_extensions,
            enabled_layers,
            library,
            max_api_version,
            _user_callbacks: user_callbacks,
        }))
    }

    /// Returns the Vulkan library used to create this instance.
    #[inline]
    pub fn library(&self) -> &Arc<VulkanLibrary> {
        &self.library
    }

    /// Returns the Vulkan version supported by the instance.
    ///
    /// This is the lower of the
    /// [driver's supported version](crate::VulkanLibrary::api_version) and
    /// [`max_api_version`](Instance::max_api_version).
    #[inline]
    pub fn api_version(&self) -> Version {
        self.api_version
    }

    /// Returns the maximum Vulkan version that was specified when creating the instance.
    #[inline]
    pub fn max_api_version(&self) -> Version {
        self.max_api_version
    }

    /// Returns pointers to the raw Vulkan functions of the instance.
    #[inline]
    pub fn fns(&self) -> &InstanceFunctions {
        &self.fns
    }

    /// Returns the extensions that have been enabled on the instance.
    #[inline]
    pub fn enabled_extensions(&self) -> &InstanceExtensions {
        &self.enabled_extensions
    }

    /// Returns the layers that have been enabled on the instance.
    #[inline]
    pub fn enabled_layers(&self) -> &[String] {
        &self.enabled_layers
    }

    /// Returns an iterator that enumerates the physical devices available.
    ///
    /// # Examples
    ///
    /// ```no_run
    /// # use vulkano::{
    /// #     instance::{Instance, InstanceExtensions},
    /// #     Version, VulkanLibrary,
    /// # };
    ///
    /// # let library = VulkanLibrary::new().unwrap();
    /// # let instance = Instance::new(library, Default::default()).unwrap();
    /// for physical_device in instance.enumerate_physical_devices().unwrap() {
    ///     println!("Available device: {}", physical_device.properties().device_name);
    /// }
    /// ```
    pub fn enumerate_physical_devices(
        self: &Arc<Self>,
    ) -> Result<impl ExactSizeIterator<Item = Arc<PhysicalDevice>>, VulkanError> {
        let fns = self.fns();

        unsafe {
            let handles = loop {
                let mut count = 0;
                (fns.v1_0.enumerate_physical_devices)(self.handle, &mut count, ptr::null_mut())
                    .result()
                    .map_err(VulkanError::from)?;

                let mut handles = Vec::with_capacity(count as usize);
                let result = (fns.v1_0.enumerate_physical_devices)(
                    self.handle,
                    &mut count,
                    handles.as_mut_ptr(),
                );

                match result {
                    ash::vk::Result::SUCCESS => {
                        handles.set_len(count as usize);
                        break handles;
                    }
                    ash::vk::Result::INCOMPLETE => (),
                    err => return Err(VulkanError::from(err)),
                }
            };

            let physical_devices: SmallVec<[_; 4]> = handles
                .into_iter()
                .map(|handle| PhysicalDevice::from_handle(self.clone(), handle))
                .collect::<Result<_, _>>()?;

            Ok(physical_devices.into_iter())
        }
    }
}

impl Drop for Instance {
    #[inline]
    fn drop(&mut self) {
        let fns = self.fns();

        unsafe {
            (fns.v1_0.destroy_instance)(self.handle, ptr::null());
        }
    }
}

unsafe impl VulkanObject for Instance {
    type Handle = ash::vk::Instance;

    #[inline]
    fn handle(&self) -> Self::Handle {
        self.handle
    }
}

impl_id_counter!(Instance);

impl Debug for Instance {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        let Self {
            handle,
            fns,
            id: _,
            api_version,
            enabled_extensions,
            enabled_layers,
            library: function_pointers,
            max_api_version,
            _user_callbacks: _,
        } = self;

        f.debug_struct("Instance")
            .field("handle", handle)
            .field("fns", fns)
            .field("api_version", api_version)
            .field("enabled_extensions", enabled_extensions)
            .field("enabled_layers", enabled_layers)
            .field("function_pointers", function_pointers)
            .field("max_api_version", max_api_version)
            .finish_non_exhaustive()
    }
}

/// Parameters to create a new `Instance`.
#[derive(Debug)]
pub struct InstanceCreateInfo {
    /// A string of your choice stating the name of your application.
    ///
    /// The default value is `None`.
    pub application_name: Option<String>,

    /// A version number of your choice specifying the version of your application.
    ///
    /// The default value is zero.
    pub application_version: Version,

    /// The extensions to enable on the instance.
    ///
    /// The default value is [`InstanceExtensions::empty()`].
    pub enabled_extensions: InstanceExtensions,

    /// The layers to enable on the instance.
    ///
    /// The default value is empty.
    pub enabled_layers: Vec<String>,

    /// A string of your choice stating the name of the engine used to power the application.
    pub engine_name: Option<String>,

    /// A version number of your choice specifying the version of the engine used to power the
    /// application.
    ///
    /// The default value is zero.
    pub engine_version: Version,

    /// The highest Vulkan API version that the application will use with the instance.
    ///
    /// Usually, you will want to leave this at the default.
    ///
    /// The default value is [`Version::HEADER_VERSION`], but if the
    /// supported instance version is 1.0, then it will be 1.0.
    pub max_api_version: Option<Version>,

    /// Include [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
    /// devices when enumerating physical devices.
    ///
    /// If you enable this flag, you must ensure that your program is prepared to handle the
    /// non-conformant aspects of these devices.
    ///
    /// If this flag is not enabled, and there are no fully-conformant devices on the system, then
    /// [`Instance::new`] will return an `IncompatibleDriver` error.
    ///
    /// The default value is `false`.
    ///
    /// # Notes
    ///
    /// If this flag is enabled, and the
    /// [`khr_portability_enumeration`](crate::instance::InstanceExtensions::khr_portability_enumeration)
    /// extension is supported, it will be enabled automatically when creating the instance.
    /// If the extension is not supported, this flag will be ignored.
    pub enumerate_portability: bool,

    /// Features of the validation layer to enable.
    ///
    /// If not empty, the
    /// [`ext_validation_features`](crate::instance::InstanceExtensions::ext_validation_features)
    /// extension must be enabled on the instance.
    pub enabled_validation_features: Vec<ValidationFeatureEnable>,

    /// Features of the validation layer to disable.
    ///
    /// If not empty, the
    /// [`ext_validation_features`](crate::instance::InstanceExtensions::ext_validation_features)
    /// extension must be enabled on the instance.
    pub disabled_validation_features: Vec<ValidationFeatureDisable>,

    pub _ne: crate::NonExhaustive,
}

impl Default for InstanceCreateInfo {
    #[inline]
    fn default() -> Self {
        Self {
            application_name: None,
            application_version: Version::major_minor(0, 0),
            enabled_extensions: InstanceExtensions::empty(),
            enabled_layers: Vec::new(),
            engine_name: None,
            engine_version: Version::major_minor(0, 0),
            max_api_version: None,
            enumerate_portability: false,
            enabled_validation_features: Vec::new(),
            disabled_validation_features: Vec::new(),
            _ne: crate::NonExhaustive(()),
        }
    }
}

impl InstanceCreateInfo {
    /// Returns an `InstanceCreateInfo` with the `application_name` and `application_version` set
    /// from information in your crate's Cargo.toml file.
    ///
    /// # Panics
    ///
    /// - Panics if the required environment variables are missing, which happens if the project
    ///   wasn't built by Cargo.
    #[inline]
    pub fn application_from_cargo_toml() -> Self {
        Self {
            application_name: Some(env!("CARGO_PKG_NAME").to_owned()),
            application_version: Version {
                major: env!("CARGO_PKG_VERSION_MAJOR").parse().unwrap(),
                minor: env!("CARGO_PKG_VERSION_MINOR").parse().unwrap(),
                patch: env!("CARGO_PKG_VERSION_PATCH").parse().unwrap(),
            },
            ..Default::default()
        }
    }
}

/// Error that can happen when creating an instance.
#[derive(Clone, Debug)]
pub enum InstanceCreationError {
    /// Not enough memory.
    OomError(OomError),

    /// Failed to initialize for an implementation-specific reason.
    InitializationFailed,

    /// One of the requested layers is missing.
    LayerNotPresent,

    /// One of the requested extensions is not supported by the implementation.
    ExtensionNotPresent,

    /// The version requested is not supported by the implementation.
    IncompatibleDriver,

    /// A restriction for an extension was not met.
    ExtensionRestrictionNotMet(ExtensionRestrictionError),

    RequirementNotMet {
        required_for: &'static str,
        requires_one_of: RequiresOneOf,
    },
}

impl Error for InstanceCreationError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            Self::OomError(err) => Some(err),
            _ => None,
        }
    }
}

impl Display for InstanceCreationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        match self {
            Self::OomError(_) => write!(f, "not enough memory available"),
            Self::InitializationFailed => write!(f, "initialization failed"),
            Self::LayerNotPresent => write!(f, "layer not present"),
            Self::ExtensionNotPresent => write!(f, "extension not present"),
            Self::IncompatibleDriver => write!(f, "incompatible driver"),
            Self::ExtensionRestrictionNotMet(err) => Display::fmt(err, f),
            Self::RequirementNotMet {
                required_for,
                requires_one_of,
            } => write!(
                f,
                "a requirement was not met for: {}; requires one of: {}",
                required_for, requires_one_of,
            ),
        }
    }
}

impl From<OomError> for InstanceCreationError {
    fn from(err: OomError) -> Self {
        Self::OomError(err)
    }
}

impl From<ExtensionRestrictionError> for InstanceCreationError {
    fn from(err: ExtensionRestrictionError) -> Self {
        Self::ExtensionRestrictionNotMet(err)
    }
}

impl From<VulkanError> for InstanceCreationError {
    fn from(err: VulkanError) -> Self {
        match err {
            err @ VulkanError::OutOfHostMemory => Self::OomError(OomError::from(err)),
            err @ VulkanError::OutOfDeviceMemory => Self::OomError(OomError::from(err)),
            VulkanError::InitializationFailed => Self::InitializationFailed,
            VulkanError::LayerNotPresent => Self::LayerNotPresent,
            VulkanError::ExtensionNotPresent => Self::ExtensionNotPresent,
            VulkanError::IncompatibleDriver => Self::IncompatibleDriver,
            _ => panic!("unexpected error: {:?}", err),
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn create_instance() {
        let _ = instance!();
    }
}
