bort_vk/
instance.rs

1use crate::ALLOCATION_CALLBACK_NONE;
2use ash::{
3    extensions::{ext, khr},
4    prelude::VkResult,
5    vk::{self, make_api_version},
6    Entry,
7};
8#[cfg(feature = "raw-window-handle-05")]
9use raw_window_handle_05::RawDisplayHandle;
10#[cfg(feature = "raw-window-handle-06")]
11use raw_window_handle_06::RawDisplayHandle;
12use std::{
13    error,
14    ffi::{CStr, CString},
15    fmt,
16    os::raw::c_char,
17    sync::Arc,
18};
19
20#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
21pub struct ApiVersion {
22    pub major: u32,
23    pub minor: u32,
24}
25
26impl ApiVersion {
27    pub const fn new(major: u32, minor: u32) -> Self {
28        Self { major, minor }
29    }
30
31    pub const fn as_vk_uint(&self) -> u32 {
32        make_api_version(0, self.major, self.minor, 0)
33    }
34}
35
36pub struct Instance {
37    inner: ash::Instance,
38    /// The highest version of vulkan that the application is designed to use.
39    /// [More info here](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkApplicationInfo.html)
40    max_api_version: ApiVersion,
41
42    // dependencies
43    entry: Arc<Entry>,
44}
45
46impl Instance {
47    /// This function will figure out the required surface extensions based on `display_handle`
48    /// e.g. VK_KHR_surface and platform specific ones like VK_KHR_win32_surface. Will also check
49    /// if the display extensions and extension_names are supported.
50    pub fn new_with_display_extensions(
51        entry: Arc<Entry>,
52        max_api_version: ApiVersion,
53        display_handle: RawDisplayHandle,
54        layer_names: Vec<CString>,
55        mut extension_names: Vec<CString>,
56    ) -> Result<Self, InstanceError> {
57        let display_extension_name_cstrs = Self::required_surface_extensions(display_handle)?;
58        let display_extension_names: Vec<CString> = display_extension_name_cstrs
59            .into_iter()
60            .map(|&cstr| cstr.to_owned())
61            .collect();
62
63        extension_names.extend_from_slice(&display_extension_names);
64
65        let unsupported_display_extensions =
66            Self::any_unsupported_extensions(&entry, None, extension_names.clone())
67                .map_err(|e| InstanceError::Creation(e))?;
68        if !unsupported_display_extensions.is_empty() {
69            return Err(InstanceError::ExtensionsNotPresent(
70                unsupported_display_extensions,
71            ));
72        }
73
74        Self::new(entry, max_api_version, layer_names, extension_names)
75    }
76
77    /// Doesn't check for extension/layer support.
78    pub fn new(
79        entry: Arc<Entry>,
80        max_api_version: ApiVersion,
81        layer_names: Vec<CString>,
82        extension_names: Vec<CString>,
83    ) -> Result<Self, InstanceError> {
84        let layer_name_ptrs: Vec<*const c_char> =
85            layer_names.iter().map(|cstring| cstring.as_ptr()).collect();
86        let extension_name_ptrs: Vec<*const c_char> = extension_names
87            .iter()
88            .map(|cstring| cstring.as_ptr())
89            .collect();
90
91        let appinfo = vk::ApplicationInfo::builder().api_version(max_api_version.as_vk_uint());
92
93        let create_info = vk::InstanceCreateInfo::builder()
94            .application_info(&appinfo)
95            .enabled_layer_names(&layer_name_ptrs)
96            .enabled_extension_names(&extension_name_ptrs);
97
98        let instance_inner =
99            unsafe { entry.create_instance(&create_info, ALLOCATION_CALLBACK_NONE) }
100                .map_err(|e| InstanceError::Creation(e))?;
101
102        Ok(Self {
103            entry,
104            inner: instance_inner,
105            max_api_version,
106        })
107    }
108
109    pub unsafe fn new_from_create_info(
110        entry: Arc<Entry>,
111        create_info_builder: vk::InstanceCreateInfoBuilder,
112    ) -> Result<Self, InstanceError> {
113        let instance_inner =
114            unsafe { entry.create_instance(&create_info_builder, ALLOCATION_CALLBACK_NONE) }
115                .map_err(|e| InstanceError::Creation(e))?;
116
117        let max_api_version = if create_info_builder.p_application_info != std::ptr::null() {
118            let api_version_combined =
119                unsafe { *create_info_builder.p_application_info }.api_version;
120            ApiVersion {
121                major: vk::api_version_major(api_version_combined),
122                minor: vk::api_version_minor(api_version_combined),
123            }
124        } else {
125            ApiVersion { major: 0, minor: 0 }
126        };
127
128        Ok(Self {
129            inner: instance_inner,
130            max_api_version,
131            entry,
132        })
133    }
134
135    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceLayerProperties.html>
136    pub fn layer_avilable(entry: &Entry, layer_name: CString) -> VkResult<bool> {
137        let layer_properties = entry.enumerate_instance_layer_properties()?;
138        let is_available = layer_properties.iter().any(|layer_prop| {
139            let installed_layer_name =
140                unsafe { CStr::from_ptr(layer_prop.layer_name.as_ptr()) }.to_owned();
141            installed_layer_name == layer_name
142        });
143        Ok(is_available)
144    }
145
146    /// Returns any of the provided `extension_names` that are unsupported by this device.
147    ///
148    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>
149    pub fn any_unsupported_extensions(
150        entry: &Entry,
151        layer_name: Option<&CStr>,
152        mut extension_names: Vec<CString>,
153    ) -> VkResult<Vec<CString>> {
154        let extension_properties = entry.enumerate_instance_extension_properties(layer_name)?;
155        extension_names.retain(|extension_name| {
156            !Self::extension_name_is_in_properties_list(
157                &extension_properties,
158                extension_name.clone(),
159            )
160        });
161        Ok(extension_names)
162    }
163
164    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>
165    pub fn supports_extension(
166        entry: &Entry,
167        layer_name: Option<&CStr>,
168        extension_name: CString,
169    ) -> VkResult<bool> {
170        let extension_properties = entry.enumerate_instance_extension_properties(layer_name)?;
171        let is_supported =
172            Self::extension_name_is_in_properties_list(&extension_properties, extension_name);
173        Ok(is_supported)
174    }
175
176    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkEnumerateInstanceExtensionProperties.html>
177    pub fn extension_name_is_in_properties_list(
178        extension_properties: &Vec<vk::ExtensionProperties>,
179        extension_name: CString,
180    ) -> bool {
181        extension_properties.iter().any(|props| {
182            let supported_extension_name =
183                unsafe { CStr::from_ptr(props.extension_name.as_ptr()) }.to_owned();
184            supported_extension_name == extension_name
185        })
186    }
187
188    /// Vulkan 1.0 features
189    pub fn physical_device_features_1_0(
190        &self,
191        physical_device_handle: vk::PhysicalDevice,
192    ) -> vk::PhysicalDeviceFeatures {
193        unsafe {
194            self.inner()
195                .get_physical_device_features(physical_device_handle)
196        }
197    }
198
199    /// Vulkan 1.1 features. If api version < 1.1, these cannot be populated.
200    pub fn physical_device_features_1_1(
201        &self,
202        physical_device_handle: vk::PhysicalDevice,
203    ) -> Option<vk::PhysicalDeviceVulkan11Features> {
204        if self.max_api_version < ApiVersion::new(1, 1) {
205            return None;
206        }
207
208        let mut features_1_1 = vk::PhysicalDeviceVulkan11Features::default();
209        let mut features = vk::PhysicalDeviceFeatures2::builder().push_next(&mut features_1_1);
210        unsafe {
211            self.inner
212                .get_physical_device_features2(physical_device_handle, &mut features)
213        };
214
215        Some(features_1_1)
216    }
217
218    /// Vulkan 1.2 features. If api version < 1.2, these cannot be populated.
219    pub fn physical_device_features_1_2(
220        &self,
221        physical_device_handle: vk::PhysicalDevice,
222    ) -> Option<vk::PhysicalDeviceVulkan12Features> {
223        if self.max_api_version < ApiVersion::new(1, 2) {
224            return None;
225        }
226
227        let mut features_1_2 = vk::PhysicalDeviceVulkan12Features::default();
228        let mut features = vk::PhysicalDeviceFeatures2::builder().push_next(&mut features_1_2);
229        unsafe {
230            self.inner
231                .get_physical_device_features2(physical_device_handle, &mut features)
232        };
233
234        Some(features_1_2)
235    }
236
237    pub fn enumerate_physical_devices(&self) -> VkResult<Vec<vk::PhysicalDevice>> {
238        unsafe { self.inner.enumerate_physical_devices() }
239    }
240
241    /// Query the required instance extensions for creating a surface from a display handle.
242    ///
243    /// This [`RawDisplayHandle`] can typically be acquired from a window, but is usually also
244    /// accessible earlier through an "event loop" concept to allow querying required instance
245    /// extensions and creation of a compatible Vulkan instance prior to creating a window.
246    ///
247    /// The returned extensions will include all extension dependencies.
248    ///
249    /// _Note: this function was copied from [ash](https://github.com/ash-rs/ash) to allow for better
250    /// dependency control._
251    pub fn required_surface_extensions(
252        display_handle: RawDisplayHandle,
253    ) -> Result<&'static [&'static CStr], InstanceError> {
254        let extensions = match display_handle {
255            RawDisplayHandle::Windows(_) => &Self::SURFACE_EXTS_WINDOWS,
256            RawDisplayHandle::Wayland(_) => &Self::SURFACE_EXTS_WAYLAND,
257            RawDisplayHandle::Xlib(_) => &Self::SURFACE_EXTS_XLIB,
258            RawDisplayHandle::Xcb(_) => &Self::SURFACE_EXTS_XCB,
259            RawDisplayHandle::Android(_) => &Self::SURFACE_EXTS_ANDROID,
260            RawDisplayHandle::AppKit(_) | RawDisplayHandle::UiKit(_) => &Self::SURFACE_EXTS_METAL,
261            _ => return Err(InstanceError::UnsupportedRawDisplayHandle),
262        };
263
264        Ok(extensions)
265    }
266
267    pub const SURFACE_EXTS_WINDOWS: [&'static CStr; 2] =
268        [khr::Surface::name(), khr::Win32Surface::name()];
269    pub const SURFACE_EXTS_WAYLAND: [&'static CStr; 2] =
270        [khr::Surface::name(), khr::WaylandSurface::name()];
271    pub const SURFACE_EXTS_XLIB: [&'static CStr; 2] =
272        [khr::Surface::name(), khr::XlibSurface::name()];
273    pub const SURFACE_EXTS_XCB: [&'static CStr; 2] =
274        [khr::Surface::name(), khr::XcbSurface::name()];
275    pub const SURFACE_EXTS_ANDROID: [&'static CStr; 2] =
276        [khr::Surface::name(), khr::AndroidSurface::name()];
277    pub const SURFACE_EXTS_METAL: [&'static CStr; 2] =
278        [khr::Surface::name(), ext::MetalSurface::name()];
279
280    // Getters
281
282    /// Access the `ash::Instance` struct that `self` contains. Allows you to access vulkan instance
283    /// functions.
284    #[inline]
285    pub fn inner(&self) -> &ash::Instance {
286        &self.inner
287    }
288
289    #[inline]
290    pub fn max_api_version(&self) -> ApiVersion {
291        self.max_api_version
292    }
293
294    #[inline]
295    pub fn entry(&self) -> &Arc<Entry> {
296        &self.entry
297    }
298}
299
300impl Drop for Instance {
301    fn drop(&mut self) {
302        unsafe {
303            self.inner.destroy_instance(ALLOCATION_CALLBACK_NONE);
304        }
305    }
306}
307
308// ~~ Error ~~
309
310#[derive(Debug, Clone)]
311pub enum InstanceError {
312    UnsupportedRawDisplayHandle,
313    ExtensionsNotPresent(Vec<CString>),
314    Creation(vk::Result),
315}
316
317impl fmt::Display for InstanceError {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        match self {
320            Self::UnsupportedRawDisplayHandle => {
321                write!(f, "unsupported display handle. could not determine the required surface extensions for this window system")
322            }
323            Self::ExtensionsNotPresent(extension_names) => {
324                write!(
325                    f,
326                    "the following extensions were reqested but are not present: {:?}",
327                    extension_names
328                )
329            }
330            Self::Creation(e) => {
331                write!(f, "failed to create device {}", e)
332            }
333        }
334    }
335}
336
337impl error::Error for InstanceError {
338    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
339        match self {
340            Self::UnsupportedRawDisplayHandle => None,
341            Self::ExtensionsNotPresent(_) => None,
342            Self::Creation(e) => Some(e),
343        }
344    }
345}
346
347// ~~ Tests ~~
348
349#[test]
350fn api_version_ordering() {
351    let ver_1_1 = ApiVersion::new(1, 1);
352    let ver_1_2 = ApiVersion::new(1, 2);
353    assert!(ver_1_1 < ver_1_2);
354}