| // 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. |
| |
| #![doc(html_logo_url = "https://raw.githubusercontent.com/vulkano-rs/vulkano/master/logo.png")] |
| //! Safe and rich Rust wrapper around the Vulkan API. |
| //! |
| //! # Starting off with Vulkano |
| //! |
| //! The steps for using Vulkan through Vulkano are in principle not any different from using |
| //! the raw Vulkan API, but the details may be different for the sake of idiomaticity, safety |
| //! and convenience. |
| //! |
| //! 1. Create a [`VulkanLibrary`]. This represents a Vulkan library on the system, which must be |
| //! loaded before you can do anything with Vulkan. |
| //! |
| //! 2. Create an [`Instance`]. This is the API entry point, and represents an initialised Vulkan |
| //! library. |
| //! |
| //! 3. If you intend to show graphics to the user on a window or a screen, create a [`Surface`]. |
| //! A `Surface` is created from a window identifier or handle, that is specific to the display or |
| //! windowing system being used. The [`vulkano-win`] crate, which is part of the Vulkano |
| //! project, can make this step easier. |
| //! |
| //! 4. [Enumerate the physical devices] that are available on the `Instance`, and choose one that |
| //! is suitable for your program. A [`PhysicalDevice`] represents a Vulkan-capable device that |
| //! is available on the system, such as a graphics card, a software implementation, etc. |
| //! |
| //! 6. Create a [`Device`] and accompanying [`Queue`]s from the selected `PhysicalDevice`. |
| //! The `Device` is the most important object of Vulkan, and you need one to create almost |
| //! every other object. `Queue`s are created together with the `Device`, and are used to submit |
| //! work to the device to make it do something. |
| //! |
| //! 7. If you created a `Surface` earlier, create a [`Swapchain`]. This object contains special |
| //! images that correspond to the contents of the surface. Whenever you want to |
| //! change the contents (show something new to the user), you must first *acquire* one of these |
| //! images from the swapchain, fill it with the new contents (by rendering, copying or any |
| //! other means), and then *present* it back to the swapchain. |
| //! A swapchain can become outdated if the properties of the surface change, such as when |
| //! the size of the window changes. It then becomes necessary to create a new swapchain. |
| //! |
| //! 8. Record a [*command buffer*](crate::command_buffer), containing commands that the device must |
| //! execute. Then build the command buffer and submit it to a `Queue`. |
| //! |
| //! Many different operations can be recorded to a command buffer, such as *draw*, *compute* and |
| //! *transfer* operations. To do any of these things, you will need to create several other objects, |
| //! depending on your specific needs. This includes: |
| //! |
| //! - [*Buffers*] store general-purpose data on memory accessible by the device. This can include |
| //! mesh data (vertices, texture coordinates etc.), lighting information, matrices, and anything |
| //! else you can think of. |
| //! |
| //! - [*Images*] store texel data, arranged in a grid of one or more dimensions. They can be used |
| //! as textures, depth/stencil buffers, framebuffers and as part of a swapchain. |
| //! |
| //! - [*Pipelines*] describe operations on the device. They include one or more [*shader*]s, small |
| //! programs that the device will execute as part of a pipeline. |
| //! Pipelines come in several types: |
| //! - A [`ComputePipeline`] describes how *dispatch* commands are to be performed. |
| //! - A [`GraphicsPipeline`] describes how *draw* commands are to be performed. |
| //! |
| //! - [*Descriptor sets*] make buffers, images and other objects available to shaders. The |
| //! arrangement of these resources in shaders is described by a [`DescriptorSetLayout`]. One or |
| //! more of these layouts in turn forms a [`PipelineLayout`], which is used when creating a |
| //! pipeline object. |
| //! |
| //! - For more complex, multi-stage draw operations, you can create a [`RenderPass`] object. |
| //! This object describes the stages, known as subpasses, that draw operations consist of, |
| //! how they interact with one another, and which types of images are available in each subpass. |
| //! You must also create a [`Framebuffer`], which contains the image objects that are to be used |
| //! in a render pass. |
| //! |
| //! # `_unchecked` functions |
| //! |
| //! Many functions in Vulkano have two versions: the normal function, which is usually safe to |
| //! call, and another function with `_unchecked` added onto the end of the name, which is unsafe |
| //! to call. The `_unchecked` functions skip all validation checks, so they are usually more |
| //! efficient, but you must ensure that you meet the validity/safety requirements of the function. |
| //! |
| //! For all `_unchecked` functions, a call to the function is valid, if a call to the |
| //! corresponding normal function with the same arguments would return without any error. |
| //! This includes following all the valid usage requirements of the Vulkan specification, but may |
| //! also include additional requirements specific to Vulkano. |
| //! **All other usage of `_unchecked` functions may be undefined behavior.** |
| //! |
| //! Because there are potentially many `_unchecked` functions, and because their name and operation |
| //! can be straightforwardly understood based on the corresponding normal function, they are hidden |
| //! from the Vulkano documentation by default. You can unhide them by enabling the |
| //! `document_unchecked` cargo feature, and then generating the documentation with the command |
| //! `cargo doc --open`. |
| //! |
| //! # Cargo features |
| //! |
| //! | Feature | Description | |
| //! |----------------------|---------------------------------------------------------------|| |
| //! | `macros` | Include reexports from [`vulkano-macros`]. Enabled by default. | |
| //! | `document_unchecked` | Include `_unchecked` functions in the generated documentation. | |
| //! | `serde` | Enables (de)serialization of certain types using [`serde`]. | |
| //! |
| //! [`VulkanLibrary`]: crate::VulkanLibrary |
| //! [`Instance`]: crate::instance::Instance |
| //! [`Surface`]: crate::swapchain::Surface |
| //! [`vulkano-win`]: https://crates.io/crates/vulkano-win |
| //! [Enumerate the physical devices]: crate::instance::Instance::enumerate_physical_devices |
| //! [`PhysicalDevice`]: crate::device::physical::PhysicalDevice |
| //! [`Device`]: crate::device::Device |
| //! [`Queue`]: crate::device::Queue |
| //! [`Swapchain`]: crate::swapchain::Swapchain |
| //! [*command buffer*]: crate::command_buffer |
| //! [*Buffers*]: crate::buffer |
| //! [*Images*]: crate::image |
| //! [*Pipelines*]: crate::pipeline |
| //! [*shader*]: crate::shader |
| //! [`ComputePipeline`]: crate::pipeline::ComputePipeline |
| //! [`GraphicsPipeline`]: crate::pipeline::GraphicsPipeline |
| //! [*Descriptor sets*]: crate::descriptor_set |
| //! [`DescriptorSetLayout`]: crate::descriptor_set::layout |
| //! [`PipelineLayout`]: crate::pipeline::layout |
| //! [`RenderPass`]: crate::render_pass::RenderPass |
| //! [`Framebuffer`]: crate::render_pass::Framebuffer |
| //! [`vulkano-macros`]: vulkano_macros |
| //! [`serde`]: https://crates.io/crates/serde |
| |
| //#![warn(missing_docs)] // TODO: activate |
| #![warn( |
| rust_2018_idioms, |
| rust_2021_compatibility, |
| clippy::trivially_copy_pass_by_ref |
| )] |
| // These lints are a bit too pedantic, so they're disabled here. |
| #![allow( |
| clippy::collapsible_else_if, |
| clippy::collapsible_if, |
| clippy::derivable_impls, // TODO: remove |
| clippy::large_enum_variant, |
| clippy::len_without_is_empty, |
| clippy::missing_safety_doc, // TODO: remove |
| clippy::module_inception, |
| clippy::mutable_key_type, |
| clippy::needless_borrowed_reference, |
| clippy::new_without_default, |
| clippy::nonminimal_bool, |
| clippy::op_ref, // Seems to be bugged, the fixed code triggers a compile error |
| clippy::result_large_err, |
| clippy::too_many_arguments, |
| clippy::type_complexity, |
| clippy::vec_box, |
| clippy::wrong_self_convention |
| )] |
| |
| pub use ash::vk::Handle; |
| pub use half; |
| pub use library::{LoadingError, VulkanLibrary}; |
| use std::{ |
| error::Error, |
| fmt::{Display, Error as FmtError, Formatter}, |
| num::NonZeroU64, |
| ops::Deref, |
| sync::Arc, |
| }; |
| pub use {extensions::ExtensionProperties, version::Version}; |
| |
| #[macro_use] |
| mod tests; |
| #[macro_use] |
| mod extensions; |
| pub mod buffer; |
| pub mod command_buffer; |
| pub mod descriptor_set; |
| pub mod device; |
| pub mod format; |
| mod version; |
| #[macro_use] |
| pub mod render_pass; |
| mod cache; |
| mod fns; |
| pub mod image; |
| pub mod instance; |
| pub mod library; |
| mod macros; |
| pub mod memory; |
| pub mod padded; |
| pub mod pipeline; |
| pub mod query; |
| mod range_map; |
| pub mod range_set; |
| pub mod sampler; |
| pub mod shader; |
| pub mod swapchain; |
| pub mod sync; |
| |
| /// Represents memory size and offset values on a Vulkan device. |
| /// Analogous to the Rust `usize` type on the host. |
| pub use ash::vk::DeviceSize; |
| |
| /// A [`DeviceSize`] that is known not to equal zero. |
| pub type NonZeroDeviceSize = NonZeroU64; |
| |
| // Allow refering to crate by its name to work around limitations of proc-macros |
| // in doctests. |
| // See https://github.com/rust-lang/cargo/issues/9886 |
| // and https://github.com/bkchr/proc-macro-crate/issues/10 |
| #[allow(unused_extern_crates)] |
| extern crate self as vulkano; |
| |
| /// Alternative to the `Deref` trait. Contrary to `Deref`, must always return the same object. |
| pub unsafe trait SafeDeref: Deref {} |
| unsafe impl<'a, T: ?Sized> SafeDeref for &'a T {} |
| unsafe impl<T: ?Sized> SafeDeref for Arc<T> {} |
| unsafe impl<T: ?Sized> SafeDeref for Box<T> {} |
| |
| /// Gives access to the internal identifier of an object. |
| pub unsafe trait VulkanObject { |
| /// The type of the object. |
| type Handle: ash::vk::Handle; |
| |
| /// Returns the raw Vulkan handle of the object. |
| fn handle(&self) -> Self::Handle; |
| } |
| |
| unsafe impl<T, U> VulkanObject for T |
| where |
| T: SafeDeref<Target = U>, |
| U: VulkanObject + ?Sized, |
| { |
| type Handle = U::Handle; |
| |
| #[inline] |
| fn handle(&self) -> Self::Handle { |
| (**self).handle() |
| } |
| } |
| |
| /// Error type returned by most Vulkan functions. |
| #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
| pub enum OomError { |
| /// There is no memory available on the host (ie. the CPU, RAM, etc.). |
| OutOfHostMemory, |
| /// There is no memory available on the device (ie. video memory). |
| OutOfDeviceMemory, |
| } |
| |
| impl Error for OomError {} |
| |
| impl Display for OomError { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| write!( |
| f, |
| "{}", |
| match self { |
| OomError::OutOfHostMemory => "no memory available on the host", |
| OomError::OutOfDeviceMemory => "no memory available on the graphical device", |
| } |
| ) |
| } |
| } |
| |
| impl From<VulkanError> for OomError { |
| fn from(err: VulkanError) -> OomError { |
| match err { |
| VulkanError::OutOfHostMemory => OomError::OutOfHostMemory, |
| VulkanError::OutOfDeviceMemory => OomError::OutOfDeviceMemory, |
| _ => panic!("unexpected error: {:?}", err), |
| } |
| } |
| } |
| |
| // Generated by build.rs |
| include!(concat!(env!("OUT_DIR"), "/errors.rs")); |
| |
| impl Error for VulkanError {} |
| |
| impl Display for VulkanError { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| write!( |
| f, |
| "{}", |
| match self { |
| VulkanError::OutOfHostMemory => "a host memory allocation has failed", |
| VulkanError::OutOfDeviceMemory => "a device memory allocation has failed", |
| VulkanError::InitializationFailed => { |
| "initialization of an object could not be completed for \ |
| implementation-specific reasons" |
| } |
| VulkanError::DeviceLost => "the logical or physical device has been lost", |
| VulkanError::MemoryMapFailed => "mapping of a memory object has failed", |
| VulkanError::LayerNotPresent => { |
| "a requested layer is not present or could not be loaded" |
| } |
| VulkanError::ExtensionNotPresent => "a requested extension is not supported", |
| VulkanError::FeatureNotPresent => "a requested feature is not supported", |
| VulkanError::IncompatibleDriver => { |
| "the requested version of Vulkan is not supported by the driver or is \ |
| otherwise incompatible for implementation-specific reasons" |
| } |
| VulkanError::TooManyObjects => { |
| "too many objects of the type have already been created" |
| } |
| VulkanError::FormatNotSupported => { |
| "a requested format is not supported on this device" |
| } |
| VulkanError::FragmentedPool => { |
| "a pool allocation has failed due to fragmentation of the pool's memory" |
| } |
| VulkanError::Unknown => { |
| "an unknown error has occurred; either the application has provided invalid \ |
| input, or an implementation failure has occurred" |
| } |
| VulkanError::OutOfPoolMemory => "a pool memory allocation has failed", |
| VulkanError::InvalidExternalHandle => { |
| "an external handle is not a valid handle of the specified type" |
| } |
| VulkanError::Fragmentation => { |
| "a descriptor pool creation has failed due to fragmentation" |
| } |
| VulkanError::InvalidOpaqueCaptureAddress => { |
| "a buffer creation or memory allocation failed because the requested address \ |
| is not available. A shader group handle assignment failed because the \ |
| requested shader group handle information is no longer valid" |
| } |
| VulkanError::IncompatibleDisplay => { |
| "the display used by a swapchain does not use the same presentable image \ |
| layout, or is incompatible in a way that prevents sharing an image" |
| } |
| VulkanError::NotPermitted => "a requested operation was not permitted", |
| VulkanError::SurfaceLost => "a surface is no longer available", |
| VulkanError::NativeWindowInUse => { |
| "the requested window is already in use by Vulkan or another API in a manner \ |
| which prevents it from being used again" |
| } |
| VulkanError::OutOfDate => { |
| "a surface has changed in such a way that it is no longer compatible with the \ |
| swapchain, and further presentation requests using the swapchain will fail" |
| } |
| VulkanError::ValidationFailed => "validation failed", |
| VulkanError::FullScreenExclusiveModeLost => { |
| "an operation on a swapchain created with application controlled full-screen \ |
| access failed as it did not have exclusive full-screen access" |
| } |
| VulkanError::InvalidDrmFormatModifierPlaneLayout => { |
| "the requested DRM format modifier plane layout is invalid" |
| } |
| VulkanError::InvalidShader => "one or more shaders failed to compile or link", |
| VulkanError::ImageUsageNotSupported => |
| "the requested `ImageUsage` are not supported", |
| VulkanError::VideoPictureLayoutNotSupported => |
| "the requested video picture layout is not supported", |
| VulkanError::VideoProfileOperationNotSupported => |
| "a video profile operation specified via \ |
| `VideoProfileInfo::video_codec_operation` is not supported", |
| VulkanError::VideoProfileFormatNotSupported => |
| "format parameters in a requested `VideoProfileInfo` chain are not supported", |
| VulkanError::VideoProfileCodecNotSupported => |
| "codec-specific parameters in a requested `VideoProfileInfo` chain are not \ |
| supported", |
| VulkanError::VideoStdVersionNotSupported => |
| "the specified video Std header version is not supported", |
| VulkanError::CompressionExhausted => |
| "an image creation failed because internal resources required for compression \ |
| are exhausted", |
| VulkanError::Unnamed(result) => |
| return write!(f, "unnamed error, VkResult value {}", result.as_raw()), |
| } |
| ) |
| } |
| } |
| |
| /// Used in errors to indicate a set of alternatives that needs to be available/enabled to allow |
| /// a given operation. |
| #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |
| pub struct RequiresOneOf { |
| /// A minimum Vulkan API version that would allow the operation. |
| pub api_version: Option<Version>, |
| |
| /// Enabled features that would allow the operation. |
| pub features: &'static [&'static str], |
| |
| /// Available/enabled device extensions that would allow the operation. |
| pub device_extensions: &'static [&'static str], |
| |
| /// Available/enabled instance extensions that would allow the operation. |
| pub instance_extensions: &'static [&'static str], |
| } |
| |
| impl RequiresOneOf { |
| /// Returns whether there is more than one possible requirement. |
| pub fn len(&self) -> usize { |
| self.api_version.map_or(0, |_| 1) |
| + self.features.len() |
| + self.device_extensions.len() |
| + self.instance_extensions.len() |
| } |
| } |
| |
| impl Display for RequiresOneOf { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| let mut members_written = 0; |
| |
| if let Some(version) = self.api_version { |
| write!(f, "Vulkan API version {}.{}", version.major, version.minor)?; |
| members_written += 1; |
| } |
| |
| if let Some((last, rest)) = self.features.split_last() { |
| if members_written != 0 { |
| write!(f, ", ")?; |
| } |
| |
| members_written += 1; |
| |
| if rest.is_empty() { |
| write!(f, "feature {}", last)?; |
| } else { |
| write!(f, "features ")?; |
| |
| for feature in rest { |
| write!(f, "{}, ", feature)?; |
| } |
| |
| write!(f, "{}", last)?; |
| } |
| } |
| |
| if let Some((last, rest)) = self.device_extensions.split_last() { |
| if members_written != 0 { |
| write!(f, ", ")?; |
| } |
| |
| members_written += 1; |
| |
| if rest.is_empty() { |
| write!(f, "device extension {}", last)?; |
| } else { |
| write!(f, "device extensions ")?; |
| |
| for feature in rest { |
| write!(f, "{}, ", feature)?; |
| } |
| |
| write!(f, "{}", last)?; |
| } |
| } |
| |
| if let Some((last, rest)) = self.instance_extensions.split_last() { |
| if members_written != 0 { |
| write!(f, ", ")?; |
| } |
| |
| if rest.is_empty() { |
| write!(f, "instance extension {}", last)?; |
| } else { |
| write!(f, "instance extensions ")?; |
| |
| for feature in rest { |
| write!(f, "{}, ", feature)?; |
| } |
| |
| write!(f, "{}", last)?; |
| } |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| #[derive(Clone, Copy, Debug)] |
| pub(crate) struct RequirementNotMet { |
| pub(crate) required_for: &'static str, |
| pub(crate) requires_one_of: RequiresOneOf, |
| } |
| |
| /// A helper type for non-exhaustive structs. |
| /// |
| /// This type cannot be constructed outside Vulkano. Structures with a field of this type can only |
| /// be constructed by calling a constructor function or `Default::default()`. The effect is similar |
| /// to the standard Rust `#[non_exhaustive]` attribute, except that it does not prevent update |
| /// syntax from being used. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] // add traits as needed |
| pub struct NonExhaustive(pub(crate) ()); |