vulkanalia/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Vulkan bindings for Rust.
4//!
5//! For a detailed guide on how this crate (thinly) wraps the Vulkan API, see
6//! the `API Concepts` section of the `Overview` chapter of the `vulkanalia`
7//! Vulkan tutorial which can be found
8//! [here](https://kylemayes.github.io/vulkanalia/overview.html#api-concepts).
9//!
10//! For the classic triangle example, see
11//! [here](https://github.com/KyleMayes/vulkanalia/tree/master/examples/src).
12//!
13//! #### Quick Tips
14//!
15//! ##### 1. Use builder structs, but don't `build` them unless you need to!
16//!
17//! `vulkanalia`'s implementation of Vulkan structs like
18//! [`vk::InstanceCreateInfo`] each have associated builder structs that make
19//! constructing instances of these structs simple (
20//! [`vk::InstanceCreateInfoBuilder`] in this case).
21//!
22//! You can call the `build` method on a builder struct to extract the Vulkan
23//! struct, but this discards valuable information! Builder structs are often
24//! parameterized with lifetimes that ensure that the values passed to the
25//! builder struct live at least as long as the builder struct itself.
26//!
27//! Any Vulkan command in `vulkanalia` that takes a Vulkan struct like
28//! [`vk::InstanceCreateInfo`] as an argument can also accept the associated
29//! builder struct as an argument. This means you can skip those `build` calls
30//! and directly invoke Vulkan commands with your builder structs so that the
31//! lifetimes can protect you from an entire class of bugs.
32//!
33//! __Don't__
34//!
35//! ```ignore
36//! let info = vk::InstanceCreateInfo::builder()
37//!     .enabled_extension_names(&vec![/* 3 extension names */])
38//!     .build();
39//!
40//! let instance = entry.create_instance(&info, None).unwrap();
41//! // 💥: this will (hopefully) crash when the Vulkan implementation attempts
42//! // to read a now deallocated list of extension names
43//! ```
44//!
45//! __Do__
46//!
47//! ```ignore
48//! let info = vk::InstanceCreateInfo::builder()
49//!     .enabled_extension_names(&vec![/* 3 extension names */]);
50//!     // Look ma, no `build`!
51//!
52//! let instance = entry.create_instance(&info, None).unwrap();
53//! // 🦀: this will raise an error at compiletime about how the reference to
54//! // the temporary list of extension names doesn't live long enough
55//! ```
56
57#![cfg_attr(not(feature = "std"), no_std)]
58#![cfg_attr(feature = "no_std_error", feature(error_in_core))]
59
60extern crate alloc;
61
62pub mod bytecode;
63pub mod chain;
64pub mod loader;
65pub mod vk;
66
67#[cfg(feature = "window")]
68pub mod window;
69
70use alloc::boxed::Box;
71use alloc::collections::btree_set::BTreeSet;
72use alloc::sync::Arc;
73use core::ffi::c_char;
74use core::fmt;
75use core::mem;
76use core::slice;
77
78use self::loader::{Loader, LoaderError};
79use self::prelude::v1_0::*;
80use self::vk::{DeviceCommands, EntryCommands, InstanceCommands, StaticCommands};
81
82/// Preludes.
83pub mod prelude {
84    /// Vulkan 1.0 prelude.
85    pub mod v1_0 {
86        pub use crate::vk;
87        pub use crate::vk::{DeviceV1_0, EntryV1_0, InstanceV1_0};
88        pub use crate::vk::{Handle, HasBuilder};
89        pub use crate::{Device, Entry, Instance, VkResult, VkSuccessResult};
90    }
91
92    /// Vulkan 1.1 prelude.
93    pub mod v1_1 {
94        pub use crate::prelude::v1_0::*;
95        pub use crate::vk::{DeviceV1_1, EntryV1_1, InstanceV1_1};
96    }
97
98    /// Vulkan 1.2 prelude.
99    pub mod v1_2 {
100        pub use crate::prelude::v1_1::*;
101        pub use crate::vk::{DeviceV1_2, EntryV1_2, InstanceV1_2};
102    }
103
104    /// Vulkan 1.3 prelude.
105    pub mod v1_3 {
106        pub use crate::prelude::v1_2::*;
107        pub use crate::vk::{DeviceV1_3, EntryV1_3, InstanceV1_3};
108    }
109
110    /// Vulkan 1.4 prelude.
111    pub mod v1_4 {
112        pub use crate::prelude::v1_3::*;
113        pub use crate::vk::{DeviceV1_4, EntryV1_4, InstanceV1_4};
114    }
115}
116
117/// The result of a executing a fallible Vulkan command.
118pub type VkResult<T> = Result<T, vk::ErrorCode>;
119/// The result of a executing a fallible Vulkan command with multiple success codes.
120pub type VkSuccessResult<T> = Result<(T, vk::SuccessCode), vk::ErrorCode>;
121
122/// An extension trait for [`vk::Result`].
123pub trait ResultExt {
124    /// Converts a [`vk::Result`] into a [`VkResult`].
125    fn result(self) -> VkResult<()>;
126}
127
128impl ResultExt for vk::Result {
129    #[inline]
130    fn result(self) -> VkResult<()> {
131        match self {
132            vk::Result::SUCCESS => Ok(()),
133            error => Err(error.into()),
134        }
135    }
136}
137
138/// A Vulkan version.
139#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
140pub struct Version {
141    /// The major version (the `x` in `x.y.z`).
142    pub major: u32,
143    /// The minor version (the `y` in `x.y.z`).
144    pub minor: u32,
145    /// The patch version (the `z` in `x.y.z`).
146    ///
147    /// If the version of Vulkan is not at least `1.1.0`, then the patch version
148    /// will not be known due to the `vkEnumerateInstanceVersion` function not
149    /// being available. In this case this field is set to `0`.
150    pub patch: u32,
151}
152
153impl Version {
154    /// The version for Vulkan `1.0.x`.
155    pub const V1_0_0: Version = Version::new(1, 0, 0);
156    /// The version for Vulkan `1.1.0`.
157    pub const V1_1_0: Version = Version::new(1, 1, 0);
158    /// The version for Vulkan `1.2.0`.
159    pub const V1_2_0: Version = Version::new(1, 2, 0);
160    /// The version for Vulkan `1.3.0`.
161    pub const V1_3_0: Version = Version::new(1, 3, 0);
162    /// The version for Vulkan `1.4.0`.
163    pub const V1_4_0: Version = Version::new(1, 4, 0);
164
165    /// Constructs a new Vulkan version.
166    pub const fn new(major: u32, minor: u32, patch: u32) -> Self {
167        Self {
168            major,
169            minor,
170            patch,
171        }
172    }
173}
174
175impl Default for Version {
176    #[inline]
177    fn default() -> Self {
178        Self::V1_0_0
179    }
180}
181
182impl fmt::Display for Version {
183    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
184        write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
185    }
186}
187
188impl From<u32> for Version {
189    #[inline]
190    fn from(version: u32) -> Self {
191        Self::new(
192            vk::version_major(version),
193            vk::version_minor(version),
194            vk::version_patch(version),
195        )
196    }
197}
198
199impl From<Version> for u32 {
200    fn from(version: Version) -> Self {
201        vk::make_version(version.major, version.minor, version.patch)
202    }
203}
204
205impl From<(u32, u32, u32)> for Version {
206    fn from((major, minor, patch): (u32, u32, u32)) -> Self {
207        Self::new(major, minor, patch)
208    }
209}
210
211impl From<Version> for (u32, u32, u32) {
212    fn from(version: Version) -> Self {
213        (version.major, version.minor, version.patch)
214    }
215}
216
217/// A Vulkan entry point.
218#[derive(Clone)]
219pub struct Entry {
220    _loader: Option<Arc<dyn Loader>>,
221    static_commands: StaticCommands,
222    commands: EntryCommands,
223}
224
225impl Entry {
226    /// Loads a Vulkan entry point from previously loaded [`StaticCommands`].
227    #[inline]
228    pub unsafe fn from_commands(static_commands: &StaticCommands) -> Self {
229        let load = |n| (static_commands.get_instance_proc_addr)(vk::Instance::null(), n);
230        let commands = EntryCommands::load(load);
231
232        Self {
233            _loader: None,
234            static_commands: *static_commands,
235            commands,
236        }
237    }
238
239    /// Loads a Vulkan entry point using a Vulkan function loader.
240    ///
241    /// # Safety
242    ///
243    /// The [`Loader::load`] method will be called on the supplied [`Loader`]
244    /// implementation to load the entry commands so the safety requirements of
245    /// [`Loader::load`] for the [`Loader`] implementation used must be upheld.
246    #[inline]
247    pub unsafe fn new(loader: impl Loader + 'static) -> Result<Self, Box<dyn LoaderError>> {
248        let loader = Arc::new(loader);
249
250        type F = extern "system" fn();
251
252        let raw = loader.load(b"vkGetInstanceProcAddr")?;
253        let get_instance_proc_addr = mem::transmute::<F, vk::PFN_vkGetInstanceProcAddr>(raw);
254
255        let raw = loader.load(b"vkGetDeviceProcAddr")?;
256        let get_device_proc_addr = mem::transmute::<F, vk::PFN_vkGetDeviceProcAddr>(raw);
257
258        let static_commands = StaticCommands {
259            get_instance_proc_addr,
260            get_device_proc_addr,
261        };
262
263        let load = |n| get_instance_proc_addr(vk::Instance::null(), n);
264        let commands = EntryCommands::load(load);
265
266        Ok(Self {
267            _loader: Some(loader),
268            static_commands,
269            commands,
270        })
271    }
272
273    /// Gets the instance-level version of this Vulkan entry point.
274    #[inline]
275    pub fn version(&self) -> VkResult<Version> {
276        unsafe { get_version(self.static_commands.get_instance_proc_addr) }
277    }
278
279    /// Creates a Vulkan instance using this Vulkan entry point.
280    ///
281    /// # Safety
282    ///
283    /// The [`Loader::load`] method will be called on the supplied [`Loader`]
284    /// implementation to load the instance commands so the safety requirements
285    /// of [`Loader::load`] for the [`Loader`] implementation used must be
286    /// upheld.
287    #[inline]
288    pub unsafe fn create_instance(
289        &self,
290        info: &vk::InstanceCreateInfo,
291        allocator: Option<&vk::AllocationCallbacks>,
292    ) -> VkResult<Instance> {
293        let instance = EntryV1_0::create_instance(self, info, allocator)?;
294        Instance::from_created(self, info, instance)
295    }
296}
297
298impl fmt::Debug for Entry {
299    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300        write!(f, "Entry")
301    }
302}
303
304unsafe impl Send for Entry {}
305unsafe impl Sync for Entry {}
306
307/// A Vulkan instance.
308#[derive(Clone)]
309pub struct Instance {
310    entry: Entry,
311    handle: vk::Instance,
312    commands: InstanceCommands,
313    version: Version,
314    extensions: BTreeSet<vk::ExtensionName>,
315    layers: BTreeSet<vk::ExtensionName>,
316}
317
318impl Instance {
319    /// Loads a Vulkan instance from a previously created [`vk::Instance`].
320    #[inline]
321    pub unsafe fn from_created(
322        entry: &Entry,
323        info: &vk::InstanceCreateInfo,
324        instance: vk::Instance,
325    ) -> VkResult<Self> {
326        let load = |n| (entry.static_commands.get_instance_proc_addr)(instance, n);
327        let commands = InstanceCommands::load(load);
328
329        let version = get_version(entry.static_commands.get_instance_proc_addr)?;
330
331        let extensions = get_names(info.enabled_extension_count, info.enabled_extension_names);
332        let layers = get_names(info.enabled_layer_count, info.enabled_layer_names);
333
334        Ok(Self {
335            entry: entry.clone(),
336            handle: instance,
337            commands,
338            version,
339            extensions,
340            layers,
341        })
342    }
343
344    /// Gets the version for this Vulkan instance.
345    #[inline]
346    pub fn version(&self) -> Version {
347        self.version
348    }
349
350    /// Gets the loaded extensions for this Vulkan instance.
351    #[inline]
352    pub fn extensions(&self) -> &BTreeSet<vk::ExtensionName> {
353        &self.extensions
354    }
355
356    /// Gets the loaded layers for this Vulkan instance.
357    #[inline]
358    pub fn layers(&self) -> &BTreeSet<vk::ExtensionName> {
359        &self.layers
360    }
361
362    /// Creates a Vulkan device using this Vulkan instance.
363    ///
364    /// # Safety
365    ///
366    /// The [`Loader::load`] method will be called on the supplied [`Loader`]
367    /// implementation to load the device commands so the safety requirements of
368    /// [`Loader::load`] for the [`Loader`] implementation used must be upheld.
369    #[inline]
370    pub unsafe fn create_device(
371        &self,
372        physical_device: vk::PhysicalDevice,
373        info: &vk::DeviceCreateInfo,
374        allocator: Option<&vk::AllocationCallbacks>,
375    ) -> VkResult<Device> {
376        let device = InstanceV1_0::create_device(self, physical_device, info, allocator)?;
377        Device::from_created(&self.entry, physical_device, info, device)
378    }
379}
380
381impl fmt::Debug for Instance {
382    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
383        f.debug_struct("Instance")
384            .field("handle", &self.handle)
385            .field("extensions", &self.extensions)
386            .field("layers", &self.layers)
387            .finish()
388    }
389}
390
391unsafe impl Send for Instance {}
392unsafe impl Sync for Instance {}
393
394/// A Vulkan device.
395#[derive(Clone)]
396pub struct Device {
397    handle: vk::Device,
398    commands: DeviceCommands,
399    physical_device: vk::PhysicalDevice,
400    extensions: BTreeSet<vk::ExtensionName>,
401    layers: BTreeSet<vk::ExtensionName>,
402}
403
404impl Device {
405    /// Loads a Vulkan device from a previously created [`vk::Device`].
406    #[inline]
407    pub unsafe fn from_created(
408        entry: &Entry,
409        physical_device: vk::PhysicalDevice,
410        info: &vk::DeviceCreateInfo,
411        device: vk::Device,
412    ) -> VkResult<Self> {
413        let load = |n| (entry.static_commands.get_device_proc_addr)(device, n);
414        let commands = DeviceCommands::load(load);
415
416        let extensions = get_names(info.enabled_extension_count, info.enabled_extension_names);
417        let layers = get_names(info.enabled_layer_count, info.enabled_layer_names);
418
419        Ok(Self {
420            handle: device,
421            commands,
422            physical_device,
423            extensions,
424            layers,
425        })
426    }
427
428    /// Gets the physical device for this Vulkan device.
429    #[inline]
430    pub fn physical_device(&self) -> vk::PhysicalDevice {
431        self.physical_device
432    }
433
434    /// Gets the loaded extensions for this Vulkan device.
435    #[inline]
436    pub fn extensions(&self) -> &BTreeSet<vk::ExtensionName> {
437        &self.extensions
438    }
439
440    /// Gets the loaded layers for this Vulkan device.
441    #[inline]
442    pub fn layers(&self) -> &BTreeSet<vk::ExtensionName> {
443        &self.layers
444    }
445}
446
447impl fmt::Debug for Device {
448    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
449        f.debug_struct("Device")
450            .field("handle", &self.handle)
451            .field("extensions", &self.extensions)
452            .field("layers", &self.layers)
453            .finish()
454    }
455}
456
457unsafe impl Send for Device {}
458unsafe impl Sync for Device {}
459
460#[inline]
461unsafe fn get_names(
462    num_strings: u32,
463    strings: *const *const c_char,
464) -> BTreeSet<vk::ExtensionName> {
465    if strings.is_null() || num_strings == 0 {
466        return BTreeSet::new();
467    }
468
469    slice::from_raw_parts(strings, num_strings as usize)
470        .iter()
471        .map(|s| vk::ExtensionName::from_ptr(*s))
472        .collect()
473}
474
475#[inline]
476unsafe fn get_version(get_instance_proc_addr: vk::PFN_vkGetInstanceProcAddr) -> VkResult<Version> {
477    let name = c"vkEnumerateInstanceVersion".as_ptr();
478    let raw = (get_instance_proc_addr)(vk::Instance::null(), name);
479    let enumerate: Option<vk::PFN_vkEnumerateInstanceVersion> = mem::transmute(raw);
480    if let Some(enumerate) = enumerate {
481        let mut version = 0;
482        match unsafe { enumerate(&mut version) } {
483            vk::Result::SUCCESS => Ok(Version::from(version)),
484            error => Err(error.into()),
485        }
486    } else {
487        Ok(Version::V1_0_0)
488    }
489}