erupt_bootstrap/
device.rs

1//! Device creation utils.
2use crate::InstanceMetadata;
3use erupt::{vk, DeviceLoader, DeviceLoaderBuilder, ExtendableFrom, InstanceLoader, LoaderError, SmallVec, ObjectHandle};
4use std::{
5    borrow::Cow,
6    collections::HashSet,
7    ffi::{CStr, CString},
8    hash::{Hash, Hasher},
9    os::raw::{c_char, c_float},
10};
11use thiserror::Error;
12
13/// Criteria for queue families.
14#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
15pub struct QueueFamilyCriteria {
16    /// A queue family will only be considered if all these flags are set.
17    pub must_support: vk::QueueFlags,
18    /// A queue family will be preferred over other ones
19    /// if all these flags are set. The more of these flags are set,
20    /// the more likely it is for the queue family to be chosen.
21    pub should_support: vk::QueueFlags,
22    /// A queue family will only be considered
23    /// if none of these flags are set.
24    pub must_not_support: vk::QueueFlags,
25    /// A queue family will be preferred over other ones
26    /// if none of these flags are set. The less of these flags are set,
27    /// the more likely it is for the queue family to be chosen.
28    pub should_not_support: vk::QueueFlags,
29    /// This criteria is only met if the presentation support matches with
30    /// this flag. `None` corresponds to being indifferent to the support.
31    /// `Some(expected)` corresponds to the criteria being met if the support
32    /// matches with `expected`.
33    pub presentation_support: Option<bool>,
34}
35
36impl QueueFamilyCriteria {
37    /// Queue family criteria that are always met.
38    #[inline]
39    pub fn none() -> QueueFamilyCriteria {
40        QueueFamilyCriteria::default()
41    }
42
43    /// The criteria are only met if the queue family supports graphics and
44    /// presentation.
45    #[inline]
46    pub fn graphics_present() -> QueueFamilyCriteria {
47        QueueFamilyCriteria::none()
48            .must_support(vk::QueueFlags::GRAPHICS)
49            .must_support_presentation()
50    }
51
52    /// Tries to match the queue family that's the closest to being a pure
53    /// transfer queue.
54    #[inline]
55    pub fn preferably_separate_transfer() -> QueueFamilyCriteria {
56        QueueFamilyCriteria::none()
57            .must_support(vk::QueueFlags::TRANSFER)
58            .should_not_support(!vk::QueueFlags::TRANSFER)
59    }
60
61    /// Add an requirement that these queue flags must be present in the
62    /// queue family.
63    #[inline]
64    pub fn must_support(mut self, must_support: vk::QueueFlags) -> QueueFamilyCriteria {
65        self.must_support |= must_support;
66        self
67    }
68
69    /// Add an recommendation that these queue flags should be present in the
70    /// queue family.
71    #[inline]
72    pub fn should_support(mut self, should_support: vk::QueueFlags) -> QueueFamilyCriteria {
73        self.should_support |= should_support;
74        self
75    }
76
77    /// Add an requirement that these queue flags must **not** be present in the
78    /// queue family.
79    #[inline]
80    pub fn must_not_support(mut self, must_not_support: vk::QueueFlags) -> QueueFamilyCriteria {
81        self.must_not_support |= must_not_support;
82        self
83    }
84
85    /// Add an recommendation that these queue flags should **not** be present in the
86    /// queue family.
87    #[inline]
88    pub fn should_not_support(mut self, should_not_support: vk::QueueFlags) -> QueueFamilyCriteria {
89        self.should_not_support |= should_not_support;
90        self
91    }
92
93    /// Require that the queue family must support presentation.
94    #[inline]
95    pub fn must_support_presentation(mut self) -> QueueFamilyCriteria {
96        self.presentation_support = Some(true);
97        self
98    }
99
100    /// Require that the queue family must not support presentation.
101    #[inline]
102    pub fn must_not_support_presentation(mut self) -> QueueFamilyCriteria {
103        self.presentation_support = Some(false);
104        self
105    }
106
107    /// Returns the index of the best suited queue family.
108    /// Returns `Ok(None)` when no queue family meets the criteria.
109    /// Returns `Err(_)` when an internal Vulkan call failed.
110    pub fn choose_queue_family<'a>(
111        &self,
112        instance: &InstanceLoader,
113        physical_device: vk::PhysicalDevice,
114        queue_family_properties: &'a [vk::QueueFamilyProperties],
115        surface: Option<vk::SurfaceKHR>,
116    ) -> Result<Option<(u32, &'a vk::QueueFamilyProperties)>, vk::Result> {
117        let mut candidates = SmallVec::new();
118        for (i, queue_family_properties) in queue_family_properties.iter().enumerate() {
119            let i = i as u32;
120
121            let positive_required = queue_family_properties
122                .queue_flags
123                .contains(self.must_support);
124
125            let negative_required = !queue_family_properties
126                .queue_flags
127                .intersects(self.must_not_support);
128
129            let presentation = || {
130                Ok(match (self.presentation_support, surface) {
131                    (None, _) => true,
132                    (Some(_), None) => false,
133                    (Some(expected), Some(surface)) => unsafe {
134                        let support = instance
135                            .get_physical_device_surface_support_khr(physical_device, i, surface)
136                            .result()?;
137
138                        support == expected
139                    },
140                })
141            };
142
143            if positive_required && negative_required && presentation()? {
144                candidates.push((i, queue_family_properties));
145            }
146        }
147
148        let best_candidate = candidates
149            .into_iter()
150            .max_by_key(|(_, queue_family_properties)| {
151                let positive_recommended = self
152                    .should_support
153                    .intersection(queue_family_properties.queue_flags)
154                    .bits()
155                    .count_ones();
156
157                let negative_recommended = self
158                    .should_not_support
159                    .difference(queue_family_properties.queue_flags)
160                    .bits()
161                    .count_ones();
162
163                positive_recommended + negative_recommended
164            });
165
166        Ok(best_candidate)
167    }
168}
169
170/// Errors that can occur during device creation.
171#[derive(Debug, Error)]
172pub enum DeviceCreationError {
173    /// Vulkan Error.
174    #[error("vulkan error")]
175    VulkanError(#[from] vk::Result),
176    /// There is no physical device at the index specified by [`DeviceBuilder::select_nth_unconditionally`].
177    #[error("no physical device at specified index")]
178    UnconditionalMissing,
179    /// No physical device met the requirements.
180    #[error("no physical device met the requirements")]
181    RequirementsNotMet,
182    /// The instance loader creation failed.
183    #[error("loader creation error")]
184    LoaderCreation(#[from] LoaderError),
185}
186
187/// Setup for [`vk::Queue`] creation. Used within [`CustomQueueSetupFn`].
188/// The [`Hash`] and [`PartialEq`] implementations on this struct **only**
189/// compare `queue_family_index`.
190#[derive(Debug, Clone)]
191pub struct QueueSetup {
192    /// Flags used to specify usage behavior of the queue.
193    pub flags: vk::DeviceQueueCreateFlags,
194    /// Index of the queue family in the queue family array.
195    pub queue_family_index: u32,
196    /// Specifies the amount of queues and the respective priority for each.
197    pub queue_priorities: Vec<c_float>,
198}
199
200impl QueueSetup {
201    /// Create a new custom queue setup with simplified arguments.
202    /// Queue priorities will all be 1.0 and all flags will be empty.
203    #[inline]
204    pub fn simple(queue_family_index: u32, queue_count: usize) -> QueueSetup {
205        QueueSetup {
206            flags: vk::DeviceQueueCreateFlags::empty(),
207            queue_family_index,
208            queue_priorities: (0..queue_count).map(|_| 1.0).collect(),
209        }
210    }
211
212    #[inline]
213    fn as_vulkan(&self) -> vk::DeviceQueueCreateInfoBuilder {
214        vk::DeviceQueueCreateInfoBuilder::new()
215            .flags(self.flags)
216            .queue_family_index(self.queue_family_index)
217            .queue_priorities(&self.queue_priorities)
218    }
219}
220
221impl PartialEq for QueueSetup {
222    fn eq(&self, rhs: &Self) -> bool {
223        self.queue_family_index == rhs.queue_family_index
224    }
225}
226
227impl Eq for QueueSetup {}
228
229impl Hash for QueueSetup {
230    fn hash<H: Hasher>(&self, state: &mut H) {
231        self.queue_family_index.hash(state);
232    }
233}
234
235/// Metadata for after device creation.
236#[derive(Debug, Clone)]
237pub struct DeviceMetadata {
238    device_handle: vk::Device,
239    physical_device: vk::PhysicalDevice,
240    properties: vk::PhysicalDeviceProperties,
241    queue_setups: SmallVec<QueueSetup>,
242    memory_properties: vk::PhysicalDeviceMemoryProperties,
243    queue_family_properties: SmallVec<vk::QueueFamilyProperties>,
244    surface: Option<vk::SurfaceKHR>,
245    enabled_extensions: SmallVec<CString>,
246}
247
248impl DeviceMetadata {
249    /// The device this metadata belongs to.
250    #[inline]
251    pub fn device_handle(&self) -> vk::Device {
252        self.device_handle
253    }
254
255    /// The physical device this device belongs to.
256    #[inline]
257    pub fn physical_device(&self) -> vk::PhysicalDevice {
258        self.physical_device
259    }
260
261    /// The surface this device was created for.
262    #[inline]
263    pub fn surface(&self) -> Option<vk::SurfaceKHR> {
264        self.surface
265    }
266
267    /// Properties of the physical device.
268    #[inline]
269    pub fn properties(&self) -> &vk::PhysicalDeviceProperties {
270        &self.properties
271    }
272
273    /// Name of the physical device.
274    #[inline]
275    pub fn device_name(&self) -> Cow<str> {
276        unsafe { CStr::from_ptr(self.properties.device_name.as_ptr()).to_string_lossy() }
277    }
278
279    /// Type of the physical device.
280    #[inline]
281    pub fn device_type(&self) -> vk::PhysicalDeviceType {
282        self.properties.device_type
283    }
284
285    /// Returns a queue and the index of the queue family it belongs to.
286    /// The best suited queue family meeting the criteria will be chosen.
287    /// `queue_index` is the index within the queue family.
288    #[inline]
289    pub fn device_queue(
290        &self,
291        instance: &InstanceLoader,
292        device: &DeviceLoader,
293        criteria: QueueFamilyCriteria,
294        queue_index: u32,
295    ) -> Result<Option<(vk::Queue, u32)>, vk::Result> {
296        let queue_family = criteria.choose_queue_family(
297            instance,
298            self.physical_device,
299            &self.queue_family_properties,
300            self.surface,
301        )?;
302
303        Ok(queue_family.and_then(|(idx, _properties)| unsafe {
304            let handle = device.get_device_queue(idx, queue_index);
305            (!handle.is_null()).then(|| (handle, idx))
306        }))
307    }
308
309    /// The queue setups which are in use.
310    #[inline]
311    pub fn queue_setups(&self) -> &[QueueSetup] {
312        &self.queue_setups
313    }
314
315    /// The memory properties of the physical device.
316    #[inline]
317    pub fn memory_properties(&self) -> &vk::PhysicalDeviceMemoryProperties {
318        &self.memory_properties
319    }
320
321    /// The queue family properties of the physical device.
322    #[inline]
323    pub fn queue_family_properties(&self) -> &[vk::QueueFamilyProperties] {
324        &self.queue_family_properties
325    }
326
327    /// List of all enabled extensions in the instance.
328    #[inline]
329    pub fn enabled_extensions(&self) -> &[CString] {
330        &self.enabled_extensions
331    }
332
333    /// Returns true if `extension` is enabled.
334    #[inline]
335    pub unsafe fn is_extension_enabled(&self, extension: *const c_char) -> bool {
336        let qry = CStr::from_ptr(extension);
337        self.enabled_extensions.iter().any(|i| i.as_c_str() == qry)
338    }
339}
340
341/// Function used to specify custom [`QueueSetup`]s, specified by
342/// [`DeviceBuilder::custom_queue_setup`].
343pub type CustomQueueSetupFn = dyn FnMut(
344    vk::PhysicalDevice,
345    &[QueueFamilyCriteria],
346    &[vk::QueueFamilyProperties],
347) -> Result<Option<HashSet<QueueSetup>>, vk::Result>;
348
349/// Suitability of a physical device.
350pub enum DeviceSuitability {
351    /// If all requirements meet this criteria, the physical device gets picked
352    /// and the search is concluded.
353    Perfect,
354    /// If any requirement meets this criteria, the physical device gets
355    /// considered but the search for a potentially perfect physical device
356    /// continues.
357    NotPreferred,
358    /// If any requirement meets this criteria, the physical device
359    /// will under no circumstances be considered.
360    Unsuitable,
361}
362
363impl From<bool> for DeviceSuitability {
364    fn from(suitable_perfect: bool) -> Self {
365        if suitable_perfect {
366            DeviceSuitability::Perfect
367        } else {
368            DeviceSuitability::Unsuitable
369        }
370    }
371}
372
373/// Function used to specify a custom additional [`DeviceSuitability`]
374/// to consider in the selection process.
375pub type AdditionalSuitabilityFn =
376    dyn FnMut(&InstanceLoader, vk::PhysicalDevice) -> DeviceSuitability;
377
378/// Allows to easily create an [`erupt::DeviceLoader`] and queues.
379pub struct DeviceBuilder<'a> {
380    loader_builder: DeviceLoaderBuilder<'a>,
381    queue_setup_fn: Option<Box<CustomQueueSetupFn>>,
382    additional_suitability_fn: Option<Box<AdditionalSuitabilityFn>>,
383    surface: Option<vk::SurfaceKHR>,
384    prioritised_device_types: SmallVec<vk::PhysicalDeviceType>,
385    queue_family_criteria: SmallVec<QueueFamilyCriteria>,
386    preferred_device_memory_size: Option<vk::DeviceSize>,
387    required_device_memory_size: Option<vk::DeviceSize>,
388    extensions: SmallVec<(*const c_char, bool)>,
389    preferred_version: Option<u32>,
390    required_version: u32,
391    required_features: Option<&'a vk::PhysicalDeviceFeatures2>,
392    unconditional_nth: Option<usize>,
393    allocator: Option<vk::AllocationCallbacks>,
394}
395
396impl<'a> DeviceBuilder<'a> {
397    /// Create a new device builder.
398    #[inline]
399    pub fn new() -> Self {
400        DeviceBuilder::with_loader_builder(DeviceLoaderBuilder::new())
401    }
402
403    /// Create a new device builder with a custom [`DeviceLoaderBuilder`].
404    pub fn with_loader_builder(loader_builder: DeviceLoaderBuilder<'a>) -> Self {
405        DeviceBuilder {
406            loader_builder,
407            queue_setup_fn: None,
408            additional_suitability_fn: None,
409            surface: None,
410            prioritised_device_types: SmallVec::new(),
411            queue_family_criteria: SmallVec::new(),
412            preferred_device_memory_size: None,
413            required_device_memory_size: None,
414            extensions: SmallVec::new(),
415            preferred_version: None,
416            required_version: vk::API_VERSION_1_0,
417            required_features: None,
418            unconditional_nth: None,
419            allocator: None,
420        }
421    }
422
423    /// Specify a custom queue setup.
424    ///
425    /// ### Default setup
426    ///
427    /// ```ignore
428    /// |physical_device, queue_family_criteria, queue_family_properties| {
429    ///     let mut queue_setup = HashSet::with_capacity(queue_family_criteria.len());
430    ///     for queue_family_criteria in queue_family_criteria {
431    ///         match queue_family_criteria.choose_queue_family(
432    ///             instance,
433    ///             physical_device,
434    ///             queue_family_properties,
435    ///             surface,
436    ///         )? {
437    ///             Some((idx, _properties)) => {
438    ///                 queue_setup.insert(QueueSetup::simple(idx, 1));
439    ///             }
440    ///             None => return Ok(None),
441    ///         }
442    ///     }
443    ///
444    ///     Ok(Some(queue_setup))
445    /// }
446    /// ```
447    #[inline]
448    pub fn custom_queue_setup(mut self, custom_queue_setup: Box<CustomQueueSetupFn>) -> Self {
449        self.queue_setup_fn = Some(custom_queue_setup);
450        self
451    }
452
453    /// Allows to specify custom criteria for a physical device.
454    /// This can for example be used to check for limits.
455    #[inline]
456    pub fn additional_suitability(
457        mut self,
458        additional_suitability: Box<AdditionalSuitabilityFn>,
459    ) -> Self {
460        self.additional_suitability_fn = Some(additional_suitability);
461        self
462    }
463
464    /// Surface to use to check for presentation support in queue families.
465    #[inline]
466    pub fn for_surface(mut self, surface: vk::SurfaceKHR) -> Self {
467        self.surface = Some(surface);
468        self
469    }
470
471    /// Prioritise devices of these types when choosing a device.
472    /// The further ahead, the higher the priority.
473    #[inline]
474    pub fn prioritise_device_types(mut self, types: &[vk::PhysicalDeviceType]) -> Self {
475        self.prioritised_device_types = types.into();
476        self
477    }
478
479    /// The queue family chosen by the criteria will be enabled on the device.
480    #[inline]
481    pub fn queue_family(mut self, criteria: QueueFamilyCriteria) -> Self {
482        self.queue_family_criteria.push(criteria);
483        self
484    }
485
486    /// Prefer a device which has at least one `DEVICE_LOCAL` memory heap with
487    /// a minimum of `size` bytes of memory.
488    #[inline]
489    pub fn prefer_device_memory_size(mut self, size: vk::DeviceSize) -> Self {
490        self.preferred_device_memory_size = Some(size);
491        self
492    }
493
494    /// Require a device which has at least one `DEVICE_LOCAL` memory heap with
495    /// a minimum of `size` bytes of memory.
496    #[inline]
497    pub fn require_device_memory_size(mut self, size: vk::DeviceSize) -> Self {
498        self.required_device_memory_size = Some(size);
499        self
500    }
501
502    /// Prefer a device which supports `extension`.
503    /// The extension will only be enabled if it's supported.
504    #[inline]
505    pub fn prefer_extension(mut self, extension: *const c_char) -> Self {
506        self.extensions.push((extension, false));
507        self
508    }
509
510    /// Require a device which supports `extension`.
511    /// The extension will be enabled.
512    #[inline]
513    pub fn require_extension(mut self, extension: *const c_char) -> Self {
514        self.extensions.push((extension, true));
515        self
516    }
517
518    /// Prefer a device which supports this version.
519    #[inline]
520    pub fn prefer_version(mut self, major: u32, minor: u32) -> Self {
521        self.preferred_version = Some(vk::make_api_version(0, major, minor, 0));
522        self
523    }
524
525    /// Prefer a device which supports this version.
526    #[inline]
527    pub fn prefer_version_raw(mut self, version: u32) -> Self {
528        self.preferred_version = Some(version);
529        self
530    }
531
532    /// Require the device to support this version.
533    #[inline]
534    pub fn require_version(mut self, major: u32, minor: u32) -> Self {
535        self.required_version = vk::make_api_version(0, major, minor, 0);
536        self
537    }
538
539    /// Require the device to support this version.
540    #[inline]
541    pub fn require_version_raw(mut self, version: u32) -> Self {
542        self.required_version = version;
543        self
544    }
545
546    /// Require these features to be present for the device.
547    /// The elements of the pointer chain will only be considered if possible.
548    /// The features will be enabled.
549    #[inline]
550    pub fn require_features(mut self, features: &'a vk::PhysicalDeviceFeatures2) -> Self {
551        self.required_features = Some(features);
552        self
553    }
554
555    /// Skip the selection logic and always select the physical device at the
556    /// specified index.
557    #[inline]
558    pub fn select_nth_unconditionally(mut self, n: usize) -> Self {
559        self.unconditional_nth = Some(n);
560        self
561    }
562
563    /// Allocation callback to use for internal Vulkan calls in the builder.
564    #[inline]
565    pub fn allocation_callbacks(mut self, allocator: vk::AllocationCallbacks) -> Self {
566        self.allocator = Some(allocator);
567        self
568    }
569
570    /// Returns the [`erupt::DeviceLoader`] and [`DeviceMetadata`], containing
571    /// the handle of the used physical device handle and its properties, as
572    /// wells as the enabled device extensions and used queue setups.
573    pub unsafe fn build(
574        mut self,
575        instance: &'a InstanceLoader,
576        instance_metadata: &InstanceMetadata,
577    ) -> Result<(DeviceLoader, DeviceMetadata), DeviceCreationError> {
578        assert_eq!(instance.handle, instance_metadata.instance_handle());
579
580        let mut queue_setup_fn = self.queue_setup_fn.unwrap_or_else(|| {
581            // properly update documentation of the `custom_queue_setup` method
582            // when changing this (formatting, used variables)
583            Box::new(
584                |physical_device, queue_family_criteria, queue_family_properties| {
585                    let mut queue_setup = HashSet::with_capacity(queue_family_criteria.len());
586                    for queue_family_criteria in queue_family_criteria {
587                        match queue_family_criteria.choose_queue_family(
588                            instance,
589                            physical_device,
590                            queue_family_properties,
591                            self.surface,
592                        )? {
593                            Some((idx, _properties)) => {
594                                queue_setup.insert(QueueSetup::simple(idx, 1));
595                            }
596                            None => return Ok(None),
597                        }
598                    }
599
600                    Ok(Some(queue_setup))
601                },
602            )
603        });
604
605        let physical_devices = instance.enumerate_physical_devices(None).result()?;
606        let mut devices_properties = physical_devices.into_iter().map(|physical_device| {
607            (physical_device, unsafe {
608                instance.get_physical_device_properties(physical_device)
609            })
610        });
611
612        let devices = if let Some(n) = self.unconditional_nth {
613            vec![devices_properties
614                .nth(n)
615                .ok_or(DeviceCreationError::UnconditionalMissing)?]
616        } else {
617            let mut device_type_preference = self.prioritised_device_types;
618            device_type_preference.extend([
619                vk::PhysicalDeviceType::DISCRETE_GPU,
620                vk::PhysicalDeviceType::INTEGRATED_GPU,
621            ]);
622
623            let mut devices_properties: Vec<_> = devices_properties.collect();
624            devices_properties.sort_by_key(|(_physical_device, properties)| {
625                device_type_preference
626                    .iter()
627                    .position(|&preference| properties.device_type == preference)
628                    .unwrap_or(usize::MAX)
629            });
630
631            devices_properties
632        };
633
634        struct Candidate {
635            physical_device: vk::PhysicalDevice,
636            properties: vk::PhysicalDeviceProperties,
637            queue_setups: SmallVec<QueueSetup>,
638            memory_properties: vk::PhysicalDeviceMemoryProperties,
639            queue_family_properties: SmallVec<vk::QueueFamilyProperties>,
640            enabled_extensions: SmallVec<*const c_char>,
641        }
642
643        let mut perfect_candidates = SmallVec::new();
644        let mut inperfect_candidates = SmallVec::new();
645        for (physical_device, properties) in devices {
646            let mut perfect_candidate = true;
647
648            if self.required_version > properties.api_version {
649                continue;
650            }
651
652            if let Some(preferred_version) = self.preferred_version {
653                if preferred_version > properties.api_version {
654                    perfect_candidate = false;
655                }
656            }
657
658            let memory_properties = instance.get_physical_device_memory_properties(physical_device);
659            let queue_family_properties =
660                instance.get_physical_device_queue_family_properties(physical_device, None);
661
662            if self.preferred_device_memory_size.is_some()
663                || self.required_device_memory_size.is_some()
664            {
665                let highest_device_local_memory = memory_properties
666                    .memory_heaps
667                    .into_iter()
668                    .take(memory_properties.memory_heap_count as usize)
669                    .filter(|memory_heap| {
670                        memory_heap
671                            .flags
672                            .contains(vk::MemoryHeapFlags::DEVICE_LOCAL)
673                    })
674                    .map(|memory_heap| memory_heap.size)
675                    .max()
676                    .expect(
677                        "spec violation: At least one heap must include \
678                            VK_MEMORY_HEAP_DEVICE_LOCAL_BIT in VkMemoryHeap::flags.",
679                    );
680
681                if let Some(preferred_device_memory_size) = self.preferred_device_memory_size {
682                    if preferred_device_memory_size > highest_device_local_memory {
683                        perfect_candidate = false;
684                    }
685                }
686
687                if let Some(required_device_memory_size) = self.required_device_memory_size {
688                    if required_device_memory_size > highest_device_local_memory {
689                        continue;
690                    }
691                }
692            }
693
694            let queue_setup = if self.queue_family_criteria.is_empty() {
695                SmallVec::new()
696            } else {
697                match queue_setup_fn(
698                    physical_device,
699                    &self.queue_family_criteria,
700                    &queue_family_properties,
701                )? {
702                    Some(queue_setup) => queue_setup.into_iter().collect(),
703                    None => continue,
704                }
705            };
706
707            let enabled_extensions = if self.extensions.is_empty() {
708                SmallVec::new()
709            } else {
710                let mut extension_properties = instance
711                    .enumerate_device_extension_properties(physical_device, None, None)
712                    .result()?;
713                for layer in instance_metadata.enabled_layers() {
714                    let cstr = layer.as_c_str();
715                    let extensions = instance
716                        .enumerate_device_extension_properties(physical_device, Some(cstr), None)
717                        .result()?;
718                    extension_properties.extend(extensions);
719                }
720
721                let mut enabled_extensions = SmallVec::new();
722                for &(extension_name, required) in self.extensions.iter() {
723                    let cstr = CStr::from_ptr(extension_name);
724                    let present = extension_properties.iter().any(|supported_extension| {
725                        CStr::from_ptr(supported_extension.extension_name.as_ptr()) == cstr
726                    });
727
728                    if present {
729                        enabled_extensions.push(extension_name);
730                    } else if required {
731                        continue;
732                    } else {
733                        perfect_candidate = false;
734                    }
735                }
736
737                enabled_extensions
738            };
739
740            let candidate = Candidate {
741                physical_device,
742                properties,
743                queue_setups: queue_setup,
744                memory_properties,
745                queue_family_properties,
746                enabled_extensions,
747            };
748
749            if let Some(additional_suitability) = self.additional_suitability_fn.as_mut() {
750                match additional_suitability(instance, physical_device) {
751                    DeviceSuitability::Perfect => (),
752                    DeviceSuitability::NotPreferred => perfect_candidate = false,
753                    DeviceSuitability::Unsuitable => continue,
754                }
755            }
756
757            if perfect_candidate {
758                perfect_candidates.push(candidate);
759            } else {
760                inperfect_candidates.push(candidate);
761            }
762        }
763
764        let features2_supported = instance_metadata.api_version_raw() >= vk::API_VERSION_1_1
765            || instance_metadata
766                .is_extension_enabled(vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
767        for candidate in perfect_candidates
768            .into_iter()
769            .chain(inperfect_candidates.into_iter())
770        {
771            let queue_create_infos: SmallVec<_> = candidate
772                .queue_setups
773                .iter()
774                .map(QueueSetup::as_vulkan)
775                .collect();
776            let mut device_info = vk::DeviceCreateInfoBuilder::new()
777                .queue_create_infos(&queue_create_infos)
778                .enabled_extension_names(&candidate.enabled_extensions);
779
780            let mut required_features;
781            if let Some(&val) = self.required_features {
782                required_features = val;
783
784                if features2_supported {
785                    device_info = device_info.extend_from(&mut required_features);
786                } else {
787                    device_info = device_info.enabled_features(&required_features.features);
788                }
789            }
790
791            let device_handle = instance
792                .create_device(
793                    candidate.physical_device,
794                    &device_info,
795                    self.allocator.as_ref(),
796                )
797                .result();
798            match device_handle {
799                Ok(device_handle) => {
800                    let device = self.loader_builder.build_with_existing_device(
801                        instance,
802                        device_handle,
803                        &device_info,
804                    )?;
805
806                    drop(queue_create_infos);
807                    let device_metadata = DeviceMetadata {
808                        device_handle,
809                        physical_device: candidate.physical_device,
810                        properties: candidate.properties,
811                        queue_setups: candidate.queue_setups,
812                        memory_properties: candidate.memory_properties,
813                        queue_family_properties: candidate.queue_family_properties,
814                        surface: self.surface,
815                        enabled_extensions: candidate
816                            .enabled_extensions
817                            .into_iter()
818                            .map(|ptr| unsafe { CStr::from_ptr(ptr).to_owned() })
819                            .collect(),
820                    };
821
822                    return Ok((device, device_metadata));
823                }
824                Err(vk::Result::ERROR_FEATURE_NOT_PRESENT) => continue,
825                Err(err) => return Err(err.into()),
826            }
827        }
828
829        Err(DeviceCreationError::RequirementsNotMet)
830    }
831}
832
833impl<'a> Default for DeviceBuilder<'a> {
834    fn default() -> Self {
835        Self::new()
836    }
837}