Skip to main content

vulkanalia_bootstrap/
device.rs

1use crate::Instance;
2use std::borrow::Cow;
3use std::cell::RefCell;
4use std::cmp::Ordering;
5use std::collections::BTreeSet;
6use std::fmt::Debug;
7use std::hint::unreachable_unchecked;
8use std::ops::Deref;
9use std::sync::Arc;
10use vulkanalia::Version;
11use vulkanalia::vk::{
12    self, DeviceV1_0, HasBuilder, InstanceV1_0, InstanceV1_1, KhrSurfaceExtensionInstanceCommands,
13};
14use vulkanalia::vk::{AllocationCallbacks, DeviceV1_1};
15
16fn supports_features(
17    supported: &vk::PhysicalDeviceFeatures,
18    requested: &vk::PhysicalDeviceFeatures,
19    features_supported: &GenericFeatureChain,
20    features_requested: &GenericFeatureChain,
21) -> bool {
22    macro_rules! check_feature {
23        ($feature: ident) => {
24            if requested.$feature == vk::TRUE && supported.$feature == vk::FALSE {
25                return false;
26            }
27        };
28    }
29
30    check_feature!(robust_buffer_access);
31    check_feature!(full_draw_index_uint32);
32    check_feature!(image_cube_array);
33    check_feature!(independent_blend);
34    check_feature!(geometry_shader);
35    check_feature!(tessellation_shader);
36    check_feature!(sample_rate_shading);
37    check_feature!(dual_src_blend);
38    check_feature!(logic_op);
39    check_feature!(multi_draw_indirect);
40    check_feature!(draw_indirect_first_instance);
41    check_feature!(depth_clamp);
42    check_feature!(depth_bias_clamp);
43    check_feature!(fill_mode_non_solid);
44    check_feature!(depth_bounds);
45    check_feature!(wide_lines);
46    check_feature!(large_points);
47    check_feature!(alpha_to_one);
48    check_feature!(multi_viewport);
49    check_feature!(sampler_anisotropy);
50    check_feature!(texture_compression_etc2);
51    check_feature!(texture_compression_astc_ldr);
52    check_feature!(texture_compression_bc);
53    check_feature!(occlusion_query_precise);
54    check_feature!(pipeline_statistics_query);
55    check_feature!(vertex_pipeline_stores_and_atomics);
56    check_feature!(fragment_stores_and_atomics);
57    check_feature!(shader_tessellation_and_geometry_point_size);
58    check_feature!(shader_image_gather_extended);
59    check_feature!(shader_storage_image_extended_formats);
60    check_feature!(shader_storage_image_multisample);
61    check_feature!(shader_storage_image_read_without_format);
62    check_feature!(shader_storage_image_write_without_format);
63    check_feature!(shader_uniform_buffer_array_dynamic_indexing);
64    check_feature!(shader_sampled_image_array_dynamic_indexing);
65    check_feature!(shader_storage_buffer_array_dynamic_indexing);
66    check_feature!(shader_storage_image_array_dynamic_indexing);
67    check_feature!(shader_clip_distance);
68    check_feature!(shader_cull_distance);
69    check_feature!(shader_float64);
70    check_feature!(shader_int64);
71    check_feature!(shader_int16);
72    check_feature!(shader_resource_residency);
73    check_feature!(shader_resource_min_lod);
74    check_feature!(sparse_binding);
75    check_feature!(sparse_residency_buffer);
76    check_feature!(sparse_residency_image_2d);
77    check_feature!(sparse_residency_image_3d);
78    check_feature!(sparse_residency2_samples);
79    check_feature!(sparse_residency4_samples);
80    check_feature!(sparse_residency8_samples);
81    check_feature!(sparse_residency16_samples);
82    check_feature!(sparse_residency_aliased);
83    check_feature!(variable_multisample_rate);
84    check_feature!(inherited_queries);
85
86    features_supported.match_all(features_requested)
87}
88
89#[inline]
90fn get_first_queue_index(
91    families: &[vk::QueueFamilyProperties],
92    desired_flags: vk::QueueFlags,
93) -> Option<usize> {
94    families
95        .iter()
96        .position(|f| f.queue_flags.contains(desired_flags))
97}
98
99/// Finds the queue which is separate from the graphics queue and has the desired flag and not the
100/// undesired flag, but will select it if no better options are available for compute support. Returns
101/// QUEUE_INDEX_MAX_VALUE if none is found.
102fn get_separate_queue_index(
103    families: &[vk::QueueFamilyProperties],
104    desired_flags: vk::QueueFlags,
105    undesired_flags: vk::QueueFlags,
106) -> Option<usize> {
107    let mut index = None;
108    for (i, family) in families.iter().enumerate() {
109        if family.queue_flags.contains(desired_flags)
110            && !family.queue_flags.contains(vk::QueueFlags::GRAPHICS)
111        {
112            if !family.queue_flags.contains(undesired_flags) {
113                return Some(i);
114            } else {
115                index = Some(i);
116            }
117        }
118    }
119
120    index
121}
122
123/// finds the first queue which supports only the desired flag (not graphics or transfer). Returns QUEUE_INDEX_MAX_VALUE if none is found.
124fn get_dedicated_queue_index(
125    families: &[vk::QueueFamilyProperties],
126    desired_flags: vk::QueueFlags,
127    undesired_flags: vk::QueueFlags,
128) -> Option<usize> {
129    families.iter().position(|f| {
130        f.queue_flags.contains(desired_flags)
131            && !f.queue_flags.contains(vk::QueueFlags::GRAPHICS)
132            && !f.queue_flags.contains(undesired_flags)
133    })
134}
135
136fn get_present_queue_index(
137    instance: &vulkanalia::Instance,
138    device: vk::PhysicalDevice,
139    surface: Option<vk::SurfaceKHR>,
140    families: &[vk::QueueFamilyProperties],
141) -> Option<usize> {
142    for (i, _) in families.iter().enumerate() {
143        if let Some(surface) = surface {
144            let present_support = unsafe {
145                instance.get_physical_device_surface_support_khr(device, i as u32, surface)
146            };
147
148            if let Ok(present_support) = present_support {
149                if present_support {
150                    return Some(i);
151                }
152            }
153        }
154    }
155
156    None
157}
158
159fn check_device_extension_support(
160    available_extensions: &BTreeSet<vk::ExtensionName>,
161    required_extensions: &BTreeSet<vk::ExtensionName>,
162) -> BTreeSet<vk::ExtensionName> {
163    let mut extensions_to_enable = BTreeSet::<vk::ExtensionName>::new();
164
165    for avail_ext in available_extensions {
166        for req_ext in required_extensions {
167            if avail_ext == req_ext {
168                extensions_to_enable.insert(*req_ext);
169                break;
170            }
171        }
172    }
173
174    extensions_to_enable
175}
176
177#[repr(u8)]
178#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
179pub enum PreferredDeviceType {
180    Other = 0,
181    Integrated = 1,
182    #[default]
183    Discrete = 2,
184    VirtualGpu = 3,
185    Cpu = 4,
186}
187
188#[derive(Default, Debug, Eq, PartialEq, Ord, PartialOrd)]
189pub enum Suitable {
190    #[default]
191    Yes,
192    Partial,
193    No,
194}
195
196#[derive(Default, Debug)]
197pub struct PhysicalDevice {
198    name: String,
199    physical_device: vk::PhysicalDevice,
200    surface: Option<vk::SurfaceKHR>,
201
202    features: vk::PhysicalDeviceFeatures,
203    pub properties: vk::PhysicalDeviceProperties,
204    memory_properties: vk::PhysicalDeviceMemoryProperties,
205    extensions_to_enable: BTreeSet<vk::ExtensionName>,
206    available_extensions: BTreeSet<vk::ExtensionName>,
207    queue_families: Vec<vk::QueueFamilyProperties>,
208    defer_surface_initialization: bool,
209    properties2_ext_enabled: bool,
210    //supported_format_properties: HashMap<vk::Format, vk::FormatProperties>,
211    suitable: Suitable,
212    supported_features_chain: GenericFeatureChain,
213    requested_features_chain: GenericFeatureChain,
214}
215
216impl AsRef<vk::PhysicalDevice> for PhysicalDevice {
217    fn as_ref(&self) -> &vk::PhysicalDevice {
218        &self.physical_device
219    }
220}
221
222impl Eq for PhysicalDevice {}
223
224impl PartialEq<Self> for PhysicalDevice {
225    fn eq(&self, other: &Self) -> bool {
226        self.name.eq(&other.name)
227            && self.physical_device.eq(&other.physical_device)
228            && self.suitable.eq(&other.suitable)
229    }
230}
231
232impl PartialOrd for PhysicalDevice {
233    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
234        Some(self.cmp(other))
235    }
236}
237
238impl Ord for PhysicalDevice {
239    fn cmp(&self, other: &Self) -> Ordering {
240        self.suitable.cmp(&other.suitable)
241    }
242}
243
244impl PhysicalDevice {
245    pub fn msaa_samples(&self) -> vk::SampleCountFlags {
246        let limits = &self.properties.limits;
247        let counts =
248            limits.framebuffer_color_sample_counts & limits.framebuffer_depth_sample_counts;
249
250        if counts.contains(vk::SampleCountFlags::_64) {
251            return vk::SampleCountFlags::_64;
252        }
253
254        if counts.contains(vk::SampleCountFlags::_32) {
255            return vk::SampleCountFlags::_32;
256        }
257
258        if counts.contains(vk::SampleCountFlags::_16) {
259            return vk::SampleCountFlags::_16;
260        }
261
262        if counts.contains(vk::SampleCountFlags::_8) {
263            return vk::SampleCountFlags::_8;
264        }
265
266        if counts.contains(vk::SampleCountFlags::_4) {
267            return vk::SampleCountFlags::_4;
268        }
269
270        if counts.contains(vk::SampleCountFlags::_2) {
271            return vk::SampleCountFlags::_2;
272        }
273
274        vk::SampleCountFlags::_1
275    }
276
277    /// If the given device extension is available on this physical device, mark it to be
278    /// enabled when creating a logical device and return true. Returns false if the
279    /// extension is not present.
280    pub fn enable_extension_if_present(&mut self, extension: vk::ExtensionName) -> bool {
281        let extension = extension.into();
282
283        if self.available_extensions.contains(&extension) {
284            self.extensions_to_enable.insert(extension)
285        } else {
286            false
287        }
288    }
289
290    /// If all extensions in `extensions` are available on this physical device, mark them to
291    /// be enabled when creating the logical device and return true. If any are missing,
292    /// nothing is enabled and false is returned.
293    pub fn enable_extensions_if_present<I: IntoIterator<Item = vk::ExtensionName>>(
294        &mut self,
295        extensions: I,
296    ) -> bool {
297        let extensions = BTreeSet::from_iter(extensions);
298        let intersection: BTreeSet<_> = self
299            .available_extensions
300            .intersection(&extensions)
301            .cloned()
302            .collect();
303
304        if intersection.len() == extensions.len() {
305            self.extensions_to_enable.extend(intersection);
306            true
307        } else {
308            false
309        }
310    }
311
312    pub fn name(&self) -> &str {
313        &self.name
314    }
315
316    pub fn api_version(&self) -> u32 {
317        self.properties.api_version
318    }
319
320    pub fn api_version_string(&self) -> String {
321        let major = vk::version_major(self.properties.api_version);
322        let minor = vk::version_minor(self.properties.api_version);
323        let patch = vk::version_patch(self.properties.api_version);
324        format!("{major}.{minor}.{patch}")
325    }
326}
327
328#[derive(Debug, Clone)]
329pub enum VulkanPhysicalDeviceFeature2 {
330    PhysicalDeviceVulkan11(vk::PhysicalDeviceVulkan11Features),
331    PhysicalDeviceVulkan12(vk::PhysicalDeviceVulkan12Features),
332    PhysicalDeviceVulkan13(vk::PhysicalDeviceVulkan13Features),
333}
334
335fn match_features(
336    requested: &VulkanPhysicalDeviceFeature2,
337    supported: &VulkanPhysicalDeviceFeature2,
338) -> bool {
339    assert_eq!(requested.s_type(), supported.s_type());
340
341    match (requested, supported) {
342        (
343            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(r),
344            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(s),
345        ) => {
346            if r.storage_buffer_16bit_access == vk::TRUE
347                && s.storage_buffer_16bit_access == vk::FALSE
348            {
349                return false;
350            }
351            if r.uniform_and_storage_buffer_16bit_access == vk::TRUE
352                && s.uniform_and_storage_buffer_16bit_access == vk::FALSE
353            {
354                return false;
355            }
356            if r.storage_push_constant16 == vk::TRUE && s.storage_push_constant16 == vk::FALSE {
357                return false;
358            }
359            if r.storage_input_output16 == vk::TRUE && s.storage_input_output16 == vk::FALSE {
360                return false;
361            }
362            if r.multiview == vk::TRUE && s.multiview == vk::FALSE {
363                return false;
364            }
365            if r.multiview_geometry_shader == vk::TRUE && s.multiview_geometry_shader == vk::FALSE {
366                return false;
367            }
368            if r.multiview_tessellation_shader == vk::TRUE
369                && s.multiview_tessellation_shader == vk::FALSE
370            {
371                return false;
372            }
373            if r.variable_pointers_storage_buffer == vk::TRUE
374                && s.variable_pointers_storage_buffer == vk::FALSE
375            {
376                return false;
377            }
378            if r.variable_pointers == vk::TRUE && s.variable_pointers == vk::FALSE {
379                return false;
380            }
381            if r.protected_memory == vk::TRUE && s.protected_memory == vk::FALSE {
382                return false;
383            }
384            if r.sampler_ycbcr_conversion == vk::TRUE && s.sampler_ycbcr_conversion == vk::FALSE {
385                return false;
386            }
387            if r.shader_draw_parameters == vk::TRUE && s.shader_draw_parameters == vk::FALSE {
388                return false;
389            }
390            true
391        }
392        (
393            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(r),
394            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(s),
395        ) => {
396            if r.sampler_mirror_clamp_to_edge == vk::TRUE
397                && s.sampler_mirror_clamp_to_edge == vk::FALSE
398            {
399                return false;
400            }
401            if r.draw_indirect_count == vk::TRUE && s.draw_indirect_count == vk::FALSE {
402                return false;
403            }
404            if r.storage_buffer_8bit_access == vk::TRUE && s.storage_buffer_8bit_access == vk::FALSE
405            {
406                return false;
407            }
408            if r.uniform_and_storage_buffer_8bit_access == vk::TRUE
409                && s.uniform_and_storage_buffer_8bit_access == vk::FALSE
410            {
411                return false;
412            }
413            if r.storage_push_constant8 == vk::TRUE && s.storage_push_constant8 == vk::FALSE {
414                return false;
415            }
416            if r.shader_buffer_int64_atomics == vk::TRUE
417                && s.shader_buffer_int64_atomics == vk::FALSE
418            {
419                return false;
420            }
421            if r.shader_shared_int64_atomics == vk::TRUE
422                && s.shader_shared_int64_atomics == vk::FALSE
423            {
424                return false;
425            }
426            if r.shader_float16 == vk::TRUE && s.shader_float16 == vk::FALSE {
427                return false;
428            }
429            if r.shader_int8 == vk::TRUE && s.shader_int8 == vk::FALSE {
430                return false;
431            }
432            if r.descriptor_indexing == vk::TRUE && s.descriptor_indexing == vk::FALSE {
433                return false;
434            }
435            if r.shader_input_attachment_array_dynamic_indexing == vk::TRUE
436                && s.shader_input_attachment_array_dynamic_indexing == vk::FALSE
437            {
438                return false;
439            }
440            if r.shader_uniform_texel_buffer_array_dynamic_indexing == vk::TRUE
441                && s.shader_uniform_texel_buffer_array_dynamic_indexing == vk::FALSE
442            {
443                return false;
444            }
445            if r.shader_storage_texel_buffer_array_dynamic_indexing == vk::TRUE
446                && s.shader_storage_texel_buffer_array_dynamic_indexing == vk::FALSE
447            {
448                return false;
449            }
450            if r.shader_uniform_buffer_array_non_uniform_indexing == vk::TRUE
451                && s.shader_uniform_buffer_array_non_uniform_indexing == vk::FALSE
452            {
453                return false;
454            }
455            if r.shader_sampled_image_array_non_uniform_indexing == vk::TRUE
456                && s.shader_sampled_image_array_non_uniform_indexing == vk::FALSE
457            {
458                return false;
459            }
460            if r.shader_storage_buffer_array_non_uniform_indexing == vk::TRUE
461                && s.shader_storage_buffer_array_non_uniform_indexing == vk::FALSE
462            {
463                return false;
464            }
465            if r.shader_storage_image_array_non_uniform_indexing == vk::TRUE
466                && s.shader_storage_image_array_non_uniform_indexing == vk::FALSE
467            {
468                return false;
469            }
470            if r.shader_input_attachment_array_non_uniform_indexing == vk::TRUE
471                && s.shader_input_attachment_array_non_uniform_indexing == vk::FALSE
472            {
473                return false;
474            }
475            if r.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::TRUE
476                && s.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::FALSE
477            {
478                return false;
479            }
480            if r.shader_storage_texel_buffer_array_non_uniform_indexing == vk::TRUE
481                && s.shader_storage_texel_buffer_array_non_uniform_indexing == vk::FALSE
482            {
483                return false;
484            }
485            if r.descriptor_binding_uniform_buffer_update_after_bind == vk::TRUE
486                && s.descriptor_binding_uniform_buffer_update_after_bind == vk::FALSE
487            {
488                return false;
489            }
490            if r.descriptor_binding_sampled_image_update_after_bind == vk::TRUE
491                && s.descriptor_binding_sampled_image_update_after_bind == vk::FALSE
492            {
493                return false;
494            }
495            if r.descriptor_binding_storage_image_update_after_bind == vk::TRUE
496                && s.descriptor_binding_storage_image_update_after_bind == vk::FALSE
497            {
498                return false;
499            }
500            if r.descriptor_binding_storage_buffer_update_after_bind == vk::TRUE
501                && s.descriptor_binding_storage_buffer_update_after_bind == vk::FALSE
502            {
503                return false;
504            }
505            if r.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::TRUE
506                && s.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::FALSE
507            {
508                return false;
509            }
510            if r.descriptor_binding_storage_texel_buffer_update_after_bind == vk::TRUE
511                && s.descriptor_binding_storage_texel_buffer_update_after_bind == vk::FALSE
512            {
513                return false;
514            }
515            if r.descriptor_binding_update_unused_while_pending == vk::TRUE
516                && s.descriptor_binding_update_unused_while_pending == vk::FALSE
517            {
518                return false;
519            }
520            if r.descriptor_binding_partially_bound == vk::TRUE
521                && s.descriptor_binding_partially_bound == vk::FALSE
522            {
523                return false;
524            }
525            if r.descriptor_binding_variable_descriptor_count == vk::TRUE
526                && s.descriptor_binding_variable_descriptor_count == vk::FALSE
527            {
528                return false;
529            }
530            if r.runtime_descriptor_array == vk::TRUE && s.runtime_descriptor_array == vk::FALSE {
531                return false;
532            }
533            if r.sampler_filter_minmax == vk::TRUE && s.sampler_filter_minmax == vk::FALSE {
534                return false;
535            }
536            if r.scalar_block_layout == vk::TRUE && s.scalar_block_layout == vk::FALSE {
537                return false;
538            }
539            if r.imageless_framebuffer == vk::TRUE && s.imageless_framebuffer == vk::FALSE {
540                return false;
541            }
542            if r.uniform_buffer_standard_layout == vk::TRUE
543                && s.uniform_buffer_standard_layout == vk::FALSE
544            {
545                return false;
546            }
547            if r.shader_subgroup_extended_types == vk::TRUE
548                && s.shader_subgroup_extended_types == vk::FALSE
549            {
550                return false;
551            }
552            if r.separate_depth_stencil_layouts == vk::TRUE
553                && s.separate_depth_stencil_layouts == vk::FALSE
554            {
555                return false;
556            }
557            if r.host_query_reset == vk::TRUE && s.host_query_reset == vk::FALSE {
558                return false;
559            }
560            if r.timeline_semaphore == vk::TRUE && s.timeline_semaphore == vk::FALSE {
561                return false;
562            }
563            if r.buffer_device_address == vk::TRUE && s.buffer_device_address == vk::FALSE {
564                return false;
565            }
566            if r.buffer_device_address_capture_replay == vk::TRUE
567                && s.buffer_device_address_capture_replay == vk::FALSE
568            {
569                return false;
570            }
571            if r.buffer_device_address_multi_device == vk::TRUE
572                && s.buffer_device_address_multi_device == vk::FALSE
573            {
574                return false;
575            }
576            if r.vulkan_memory_model == vk::TRUE && s.vulkan_memory_model == vk::FALSE {
577                return false;
578            }
579            if r.vulkan_memory_model_device_scope == vk::TRUE
580                && s.vulkan_memory_model_device_scope == vk::FALSE
581            {
582                return false;
583            }
584            if r.vulkan_memory_model_availability_visibility_chains == vk::TRUE
585                && s.vulkan_memory_model_availability_visibility_chains == vk::FALSE
586            {
587                return false;
588            }
589            if r.shader_output_viewport_index == vk::TRUE
590                && s.shader_output_viewport_index == vk::FALSE
591            {
592                return false;
593            }
594            if r.shader_output_layer == vk::TRUE && s.shader_output_layer == vk::FALSE {
595                return false;
596            }
597            if r.subgroup_broadcast_dynamic_id == vk::TRUE
598                && s.subgroup_broadcast_dynamic_id == vk::FALSE
599            {
600                return false;
601            }
602            true
603        }
604        (
605            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(r),
606            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(s),
607        ) => {
608            if r.robust_image_access == vk::TRUE && s.robust_image_access == vk::FALSE {
609                return false;
610            }
611            if r.inline_uniform_block == vk::TRUE && s.inline_uniform_block == vk::FALSE {
612                return false;
613            }
614            if r.descriptor_binding_inline_uniform_block_update_after_bind == vk::TRUE
615                && s.descriptor_binding_inline_uniform_block_update_after_bind == vk::FALSE
616            {
617                return false;
618            }
619            if r.pipeline_creation_cache_control == vk::TRUE
620                && s.pipeline_creation_cache_control == vk::FALSE
621            {
622                return false;
623            }
624            if r.private_data == vk::TRUE && s.private_data == vk::FALSE {
625                return false;
626            }
627            if r.shader_demote_to_helper_invocation == vk::TRUE
628                && s.shader_demote_to_helper_invocation == vk::FALSE
629            {
630                return false;
631            }
632            if r.shader_terminate_invocation == vk::TRUE
633                && s.shader_terminate_invocation == vk::FALSE
634            {
635                return false;
636            }
637            if r.subgroup_size_control == vk::TRUE && s.subgroup_size_control == vk::FALSE {
638                return false;
639            }
640            if r.compute_full_subgroups == vk::TRUE && s.compute_full_subgroups == vk::FALSE {
641                return false;
642            }
643            if r.synchronization2 == vk::TRUE && s.synchronization2 == vk::FALSE {
644                return false;
645            }
646            if r.texture_compression_astc_hdr == vk::TRUE
647                && s.texture_compression_astc_hdr == vk::FALSE
648            {
649                return false;
650            }
651            if r.shader_zero_initialize_workgroup_memory == vk::TRUE
652                && s.shader_zero_initialize_workgroup_memory == vk::FALSE
653            {
654                return false;
655            }
656            if r.dynamic_rendering == vk::TRUE && s.dynamic_rendering == vk::FALSE {
657                return false;
658            }
659            if r.shader_integer_dot_product == vk::TRUE && s.shader_integer_dot_product == vk::FALSE
660            {
661                return false;
662            }
663            if r.maintenance4 == vk::TRUE && s.maintenance4 == vk::FALSE {
664                return false;
665            }
666            true
667        }
668        _ => unsafe { unreachable_unchecked() },
669    }
670}
671impl<'a> VulkanPhysicalDeviceFeature2 {
672    fn combine(&mut self, other: &VulkanPhysicalDeviceFeature2) {
673        assert_eq!(self.s_type(), other.s_type());
674
675        match (self, other) {
676            (
677                Self::PhysicalDeviceVulkan11(f),
678                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(other),
679            ) => {
680                f.storage_buffer_16bit_access |= other.storage_buffer_16bit_access;
681                f.uniform_and_storage_buffer_16bit_access |=
682                    other.uniform_and_storage_buffer_16bit_access;
683                f.storage_push_constant16 |= other.storage_push_constant16;
684                f.storage_input_output16 |= other.storage_input_output16;
685                f.multiview |= other.multiview;
686                f.multiview_geometry_shader |= other.multiview_geometry_shader;
687                f.multiview_tessellation_shader |= other.multiview_tessellation_shader;
688                f.variable_pointers_storage_buffer |= other.variable_pointers_storage_buffer;
689                f.variable_pointers |= other.variable_pointers;
690                f.protected_memory |= other.protected_memory;
691                f.sampler_ycbcr_conversion |= other.sampler_ycbcr_conversion;
692                f.shader_draw_parameters |= other.shader_draw_parameters;
693            }
694            (
695                Self::PhysicalDeviceVulkan12(f),
696                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(other),
697            ) => {
698                f.sampler_mirror_clamp_to_edge |= other.sampler_mirror_clamp_to_edge;
699                f.draw_indirect_count |= other.draw_indirect_count;
700                f.storage_buffer_8bit_access |= other.storage_buffer_8bit_access;
701                f.uniform_and_storage_buffer_8bit_access |=
702                    other.uniform_and_storage_buffer_8bit_access;
703                f.storage_push_constant8 |= other.storage_push_constant8;
704                f.shader_buffer_int64_atomics |= other.shader_buffer_int64_atomics;
705                f.shader_shared_int64_atomics |= other.shader_shared_int64_atomics;
706                f.shader_float16 |= other.shader_float16;
707                f.shader_int8 |= other.shader_int8;
708                f.descriptor_indexing |= other.descriptor_indexing;
709                f.shader_input_attachment_array_dynamic_indexing |=
710                    other.shader_input_attachment_array_dynamic_indexing;
711                f.shader_uniform_texel_buffer_array_dynamic_indexing |=
712                    other.shader_uniform_texel_buffer_array_dynamic_indexing;
713                f.shader_storage_texel_buffer_array_dynamic_indexing |=
714                    other.shader_storage_texel_buffer_array_dynamic_indexing;
715                f.shader_uniform_buffer_array_non_uniform_indexing |=
716                    other.shader_uniform_buffer_array_non_uniform_indexing;
717                f.shader_sampled_image_array_non_uniform_indexing |=
718                    other.shader_sampled_image_array_non_uniform_indexing;
719                f.shader_storage_buffer_array_non_uniform_indexing |=
720                    other.shader_storage_buffer_array_non_uniform_indexing;
721                f.shader_storage_image_array_non_uniform_indexing |=
722                    other.shader_storage_image_array_non_uniform_indexing;
723                f.shader_input_attachment_array_non_uniform_indexing |=
724                    other.shader_input_attachment_array_non_uniform_indexing;
725                f.shader_uniform_texel_buffer_array_non_uniform_indexing |=
726                    other.shader_uniform_texel_buffer_array_non_uniform_indexing;
727                f.shader_storage_texel_buffer_array_non_uniform_indexing |=
728                    other.shader_storage_texel_buffer_array_non_uniform_indexing;
729                f.descriptor_binding_uniform_buffer_update_after_bind |=
730                    other.descriptor_binding_uniform_buffer_update_after_bind;
731                f.descriptor_binding_sampled_image_update_after_bind |=
732                    other.descriptor_binding_sampled_image_update_after_bind;
733                f.descriptor_binding_storage_image_update_after_bind |=
734                    other.descriptor_binding_storage_image_update_after_bind;
735                f.descriptor_binding_storage_buffer_update_after_bind |=
736                    other.descriptor_binding_storage_buffer_update_after_bind;
737                f.descriptor_binding_uniform_texel_buffer_update_after_bind |=
738                    other.descriptor_binding_uniform_texel_buffer_update_after_bind;
739                f.descriptor_binding_storage_texel_buffer_update_after_bind |=
740                    other.descriptor_binding_storage_texel_buffer_update_after_bind;
741                f.descriptor_binding_update_unused_while_pending |=
742                    other.descriptor_binding_update_unused_while_pending;
743                f.descriptor_binding_partially_bound |= other.descriptor_binding_partially_bound;
744                f.descriptor_binding_variable_descriptor_count |=
745                    other.descriptor_binding_variable_descriptor_count;
746                f.runtime_descriptor_array |= other.runtime_descriptor_array;
747                f.sampler_filter_minmax |= other.sampler_filter_minmax;
748                f.scalar_block_layout |= other.scalar_block_layout;
749                f.imageless_framebuffer |= other.imageless_framebuffer;
750                f.uniform_buffer_standard_layout |= other.uniform_buffer_standard_layout;
751                f.shader_subgroup_extended_types |= other.shader_subgroup_extended_types;
752                f.separate_depth_stencil_layouts |= other.separate_depth_stencil_layouts;
753                f.host_query_reset |= other.host_query_reset;
754                f.timeline_semaphore |= other.timeline_semaphore;
755                f.buffer_device_address |= other.buffer_device_address;
756                f.buffer_device_address_capture_replay |=
757                    other.buffer_device_address_capture_replay;
758                f.buffer_device_address_multi_device |= other.buffer_device_address_multi_device;
759                f.vulkan_memory_model |= other.vulkan_memory_model;
760                f.vulkan_memory_model_device_scope |= other.vulkan_memory_model_device_scope;
761                f.vulkan_memory_model_availability_visibility_chains |=
762                    other.vulkan_memory_model_availability_visibility_chains;
763                f.shader_output_viewport_index |= other.shader_output_viewport_index;
764                f.shader_output_layer |= other.shader_output_layer;
765                f.subgroup_broadcast_dynamic_id |= other.subgroup_broadcast_dynamic_id;
766            }
767            (
768                Self::PhysicalDeviceVulkan13(f),
769                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(other),
770            ) => {
771                f.robust_image_access |= other.robust_image_access;
772                f.inline_uniform_block |= other.inline_uniform_block;
773                f.descriptor_binding_inline_uniform_block_update_after_bind |=
774                    other.descriptor_binding_inline_uniform_block_update_after_bind;
775                f.pipeline_creation_cache_control |= other.pipeline_creation_cache_control;
776                f.private_data |= other.private_data;
777                f.shader_demote_to_helper_invocation |= other.shader_demote_to_helper_invocation;
778                f.shader_terminate_invocation |= other.shader_terminate_invocation;
779                f.subgroup_size_control |= other.subgroup_size_control;
780                f.compute_full_subgroups |= other.compute_full_subgroups;
781                f.synchronization2 |= other.synchronization2;
782                f.texture_compression_astc_hdr |= other.texture_compression_astc_hdr;
783                f.shader_zero_initialize_workgroup_memory |=
784                    other.shader_zero_initialize_workgroup_memory;
785                f.dynamic_rendering |= other.dynamic_rendering;
786                f.shader_integer_dot_product |= other.shader_integer_dot_product;
787                f.maintenance4 |= other.maintenance4;
788            }
789            _ => unsafe { unreachable_unchecked() },
790        }
791    }
792
793    fn s_type(&self) -> vk::StructureType {
794        match self {
795            Self::PhysicalDeviceVulkan11(f) => f.s_type,
796            Self::PhysicalDeviceVulkan12(f) => f.s_type,
797            Self::PhysicalDeviceVulkan13(f) => f.s_type,
798        }
799    }
800}
801
802impl From<vk::PhysicalDeviceVulkan11Features> for VulkanPhysicalDeviceFeature2 {
803    fn from(value: vk::PhysicalDeviceVulkan11Features) -> Self {
804        Self::PhysicalDeviceVulkan11(value)
805    }
806}
807
808impl From<vk::PhysicalDeviceVulkan12Features> for VulkanPhysicalDeviceFeature2 {
809    fn from(value: vk::PhysicalDeviceVulkan12Features) -> Self {
810        Self::PhysicalDeviceVulkan12(value)
811    }
812}
813
814impl From<vk::PhysicalDeviceVulkan13Features> for VulkanPhysicalDeviceFeature2 {
815    fn from(value: vk::PhysicalDeviceVulkan13Features) -> Self {
816        Self::PhysicalDeviceVulkan13(value)
817    }
818}
819//endregion vulkanfeatures
820
821#[derive(Debug, Clone, Default)]
822struct GenericFeatureChain {
823    nodes: Vec<VulkanPhysicalDeviceFeature2>,
824}
825
826impl Deref for GenericFeatureChain {
827    type Target = Vec<VulkanPhysicalDeviceFeature2>;
828
829    fn deref(&self) -> &Self::Target {
830        &self.nodes
831    }
832}
833
834impl GenericFeatureChain {
835    fn new() -> Self {
836        Self { nodes: vec![] }
837    }
838
839    fn add(&mut self, feature: impl Into<VulkanPhysicalDeviceFeature2>) {
840        let new_node = feature.into();
841
842        for node in &mut self.nodes {
843            if new_node.s_type() == node.s_type() {
844                node.combine(&new_node);
845                return;
846            }
847        }
848
849        self.nodes.push(new_node);
850    }
851
852    fn match_all(&self, features_requested: &GenericFeatureChain) -> bool {
853        if features_requested.nodes.len() != self.nodes.len() {
854            return false;
855        }
856
857        let features_requested = features_requested.nodes.as_slice();
858        let features = self.nodes.as_slice();
859
860        for (requested_node, node) in features_requested.iter().zip(features) {
861            if !match_features(requested_node, node) {
862                return false;
863            }
864        }
865
866        true
867    }
868}
869
870#[derive(Debug)]
871struct SelectionCriteria {
872    name: String,
873    preferred_device_type: PreferredDeviceType,
874    allow_any_type: bool,
875    require_present: bool,
876    require_dedicated_transfer_queue: bool,
877    require_dedicated_compute_queue: bool,
878    require_separate_transfer_queue: bool,
879    require_separate_compute_queue: bool,
880    required_mem_size: vk::DeviceSize,
881    required_extensions: BTreeSet<vk::ExtensionName>,
882    required_version: Version,
883    required_features: vk::PhysicalDeviceFeatures,
884    required_formats: Vec<vk::Format>,
885    requested_features_chain: RefCell<GenericFeatureChain>,
886    defer_surface_initialization: bool,
887    use_first_gpu_unconditionally: bool,
888    enable_portability_subset: bool,
889}
890
891impl Default for SelectionCriteria {
892    fn default() -> Self {
893        Self {
894            name: String::new(),
895            preferred_device_type: PreferredDeviceType::Discrete,
896            allow_any_type: true,
897            require_present: true,
898            require_dedicated_transfer_queue: false,
899            require_dedicated_compute_queue: false,
900            require_separate_transfer_queue: false,
901            require_separate_compute_queue: false,
902            required_mem_size: 0,
903            required_extensions: BTreeSet::new(),
904            required_version: Version::V1_0_0,
905            required_features: vk::PhysicalDeviceFeatures::default(),
906            defer_surface_initialization: false,
907            use_first_gpu_unconditionally: false,
908            enable_portability_subset: true,
909            requested_features_chain: RefCell::new(GenericFeatureChain::new()),
910            required_formats: vec![],
911        }
912    }
913}
914
915pub struct PhysicalDeviceSelector {
916    instance: Arc<Instance>,
917    surface: Option<vk::SurfaceKHR>,
918    selection_criteria: SelectionCriteria,
919}
920
921impl PhysicalDeviceSelector {
922    /// Create a new `PhysicalDeviceSelector` for the provided `Instance`.
923    ///
924    /// The selector can be configured with builder-style methods before calling `select`.
925    pub fn new(instance: Arc<Instance>) -> PhysicalDeviceSelector {
926        let enable_portability_subset = cfg!(feature = "portability");
927        let require_present = instance.surface.is_some();
928        let required_version = instance.api_version;
929        Self {
930            surface: instance.surface,
931            instance,
932            selection_criteria: SelectionCriteria {
933                require_present,
934                required_version,
935                enable_portability_subset,
936                ..Default::default()
937            },
938        }
939    }
940
941    /// Specify a surface to use when evaluating device presentation support.
942    pub fn surface(mut self, surface: vk::SurfaceKHR) -> Self {
943        self.surface.replace(surface);
944        self
945    }
946
947    /// Add an additional device feature (vulkan feature2 struct) that must be supported by
948    /// the physical device in order to be selected.
949    pub fn add_required_extension_feature<T: Into<VulkanPhysicalDeviceFeature2>>(
950        self,
951        feature: T,
952    ) -> Self {
953        self.selection_criteria
954            .requested_features_chain
955            .borrow_mut()
956            .add(feature);
957        self
958    }
959
960    /// Require the given `vk::PhysicalDeviceFeatures` when selecting a physical device.
961    pub fn add_required_features(mut self, features: vk::PhysicalDeviceFeatures) -> Self {
962        self.selection_criteria.required_features = features;
963        self
964    }
965
966    /// Restrict selection to devices whose name matches `name`.
967    pub fn name(mut self, name: impl Into<String>) -> Self {
968        self.selection_criteria.name = name.into();
969        self
970    }
971
972    /// Prefer devices of the given `PreferredDeviceType` when ranking candidates.
973    pub fn preferred_device_type(mut self, device_type: PreferredDeviceType) -> Self {
974        self.selection_criteria.preferred_device_type = device_type;
975        self
976    }
977
978    /// Allow devices of any GPU type (when true) or restrict to the preferred device type.
979    pub fn allow_any_gpu_device_type(mut self, allow: bool) -> Self {
980        self.selection_criteria.allow_any_type = allow;
981        self
982    }
983
984    /// Require a dedicated transfer-only queue family to be present on the physical device.
985    pub fn require_dedicated_transfer_queue(mut self, require: bool) -> Self {
986        self.selection_criteria.require_dedicated_transfer_queue = require;
987        self
988    }
989
990    /// Require a dedicated compute-only queue family to be present on the physical device.
991    pub fn require_dedicated_compute_queue(mut self, require: bool) -> Self {
992        self.selection_criteria.require_dedicated_compute_queue = require;
993        self
994    }
995
996    /// Require a queue family separate from graphics for transfer operations.
997    pub fn require_separate_transfer_queue(mut self, require: bool) -> Self {
998        self.selection_criteria.require_separate_transfer_queue = require;
999        self
1000    }
1001
1002    /// Require a queue family separate from graphics for compute operations.
1003    pub fn require_separate_compute_queue(mut self, require: bool) -> Self {
1004        self.selection_criteria.require_separate_compute_queue = require;
1005        self
1006    }
1007
1008    /// Require the device to have at least `required` bytes of device-local memory.
1009    pub fn required_device_memory_size(mut self, required: vk::DeviceSize) -> Self {
1010        self.selection_criteria.required_mem_size = required;
1011        self
1012    }
1013
1014    /// Require support for the provided list of `vk::Format`s on the device's surface.
1015    pub fn required_formats(mut self, required: impl IntoIterator<Item = vk::Format>) -> Self {
1016        self.selection_criteria.required_formats = required.into_iter().collect();
1017        self
1018    }
1019
1020    /// If `select` is true, automatically select the first enumerated physical device
1021    /// without applying suitability checks.
1022    pub fn select_first_device_unconditionally(mut self, select: bool) -> Self {
1023        self.selection_criteria.use_first_gpu_unconditionally = select;
1024        self
1025    }
1026
1027    fn set_is_suitable(&self, device: &mut PhysicalDevice) {
1028        let criteria = &self.selection_criteria;
1029
1030        let device_name = device.properties.device_name.to_string_lossy();
1031
1032        if !criteria.name.is_empty() && Cow::Borrowed(&criteria.name) != device_name {
1033            #[cfg(feature = "enable_tracing")]
1034            {
1035                tracing::warn!(
1036                    "Device {} is not suitable. Name requested: {}",
1037                    device_name,
1038                    criteria.name
1039                );
1040            }
1041            device.suitable = Suitable::No;
1042            return;
1043        };
1044
1045        if u32::from(criteria.required_version) > device.properties.api_version {
1046            #[cfg(feature = "enable_tracing")]
1047            {
1048                let requested_version = criteria.required_version;
1049                let available_version = device.properties.api_version;
1050                tracing::warn!(
1051                    "Device {} is not suitable. Requested version: {}, Available version: {}",
1052                    device_name,
1053                    requested_version,
1054                    available_version
1055                );
1056            }
1057            device.suitable = Suitable::No;
1058            return;
1059        }
1060
1061        let dedicated_compute = get_dedicated_queue_index(
1062            &device.queue_families,
1063            vk::QueueFlags::COMPUTE,
1064            vk::QueueFlags::TRANSFER,
1065        );
1066
1067        let dedicated_transfer = get_dedicated_queue_index(
1068            &device.queue_families,
1069            vk::QueueFlags::TRANSFER,
1070            vk::QueueFlags::COMPUTE,
1071        );
1072
1073        let separate_compute = get_separate_queue_index(
1074            &device.queue_families,
1075            vk::QueueFlags::COMPUTE,
1076            vk::QueueFlags::TRANSFER,
1077        );
1078
1079        let separate_transfer = get_separate_queue_index(
1080            &device.queue_families,
1081            vk::QueueFlags::TRANSFER,
1082            vk::QueueFlags::COMPUTE,
1083        );
1084
1085        let present_queue = get_present_queue_index(
1086            &self.instance.instance,
1087            device.physical_device,
1088            self.surface,
1089            &device.queue_families,
1090        );
1091
1092        if criteria.require_dedicated_compute_queue && dedicated_compute.is_none() {
1093            device.suitable = Suitable::No;
1094            return;
1095        }
1096
1097        if criteria.require_dedicated_transfer_queue && dedicated_transfer.is_none() {
1098            device.suitable = Suitable::No;
1099            return;
1100        }
1101
1102        if criteria.require_separate_transfer_queue && separate_transfer.is_none() {
1103            device.suitable = Suitable::No;
1104            return;
1105        }
1106
1107        if criteria.require_separate_compute_queue && separate_compute.is_none() {
1108            device.suitable = Suitable::No;
1109            return;
1110        }
1111
1112        if criteria.require_present
1113            && present_queue.is_none()
1114            && !criteria.defer_surface_initialization
1115        {
1116            device.suitable = Suitable::No;
1117            return;
1118        }
1119
1120        let required_extensions_supported = check_device_extension_support(
1121            &device.available_extensions,
1122            &criteria.required_extensions,
1123        );
1124
1125        if required_extensions_supported.len() != criteria.required_extensions.len() {
1126            device.suitable = Suitable::No;
1127            return;
1128        }
1129
1130        if !criteria.defer_surface_initialization && criteria.require_present {
1131            if let Some(surface) = self.surface {
1132                let formats = unsafe {
1133                    self.instance
1134                        .instance
1135                        .get_physical_device_surface_formats_khr(device.physical_device, surface)
1136                };
1137                let Ok(formats) = formats else {
1138                    device.suitable = Suitable::No;
1139                    return;
1140                };
1141
1142                let present_modes = unsafe {
1143                    self.instance
1144                        .instance
1145                        .get_physical_device_surface_present_modes_khr(
1146                            device.physical_device,
1147                            surface,
1148                        )
1149                };
1150                let Ok(present_modes) = present_modes else {
1151                    device.suitable = Suitable::No;
1152                    return;
1153                };
1154
1155                if present_modes.is_empty() || formats.is_empty() {
1156                    device.suitable = Suitable::No;
1157                    return;
1158                }
1159            };
1160        };
1161
1162        let preferred_device_type =
1163            vk::PhysicalDeviceType::from_raw(criteria.preferred_device_type as u8 as i32);
1164        if !criteria.allow_any_type && device.properties.device_type != preferred_device_type {
1165            device.suitable = Suitable::Partial;
1166        }
1167
1168        let required_features_supported = supports_features(
1169            &device.features,
1170            &criteria.required_features,
1171            &device.supported_features_chain,
1172            &criteria.requested_features_chain.borrow(),
1173        );
1174
1175        if !required_features_supported {
1176            device.suitable = Suitable::No;
1177            return;
1178        }
1179
1180        //let supported_formats = &device.format_properties;
1181
1182        for memory_heap in device.memory_properties.memory_heaps {
1183            if memory_heap
1184                .flags
1185                .contains(vk::MemoryHeapFlags::DEVICE_LOCAL)
1186                && memory_heap.size < criteria.required_mem_size
1187            {
1188                device.suitable = Suitable::No;
1189                return;
1190            }
1191        }
1192    }
1193
1194    fn populate_device_details(
1195        &self,
1196        vk_phys_device: vk::PhysicalDevice,
1197    ) -> crate::Result<PhysicalDevice> {
1198        let instance = self.instance.as_ref();
1199        let criteria = &self.selection_criteria;
1200
1201        let mut physical_device = PhysicalDevice {
1202            physical_device: vk_phys_device,
1203            surface: instance.surface,
1204            defer_surface_initialization: criteria.defer_surface_initialization,
1205            queue_families: unsafe {
1206                instance
1207                    .instance
1208                    .get_physical_device_queue_family_properties(vk_phys_device)
1209            },
1210            properties: unsafe {
1211                instance
1212                    .instance
1213                    .get_physical_device_properties(vk_phys_device)
1214            },
1215            features: unsafe {
1216                instance
1217                    .instance
1218                    .get_physical_device_features(vk_phys_device)
1219            },
1220            memory_properties: unsafe {
1221                instance
1222                    .instance
1223                    .get_physical_device_memory_properties(vk_phys_device)
1224            },
1225            // supported_format_properties: {
1226            //     // vulkan has 185 formats in ash
1227            //     let range = 0..185;
1228            //     range
1229            //         .filter_map(|format| {
1230            //             let format = vk::Format::from_raw(format);
1231            //             let format_properties = unsafe {
1232            //                 instance
1233            //                     .instance
1234            //                     .get_physical_device_format_properties(vk_phys_device, format)
1235            //             };
1236            //             if !format_properties.optimal_tiling_features.is_empty()
1237            //                 || !format_properties.buffer_features.is_empty()
1238            //                 || !format_properties.linear_tiling_features.is_empty()
1239            //             {
1240            //                 Some((format, format_properties))
1241            //             } else {
1242            //                 None
1243            //             }
1244            //         })
1245            //         .collect()
1246            // },
1247            properties2_ext_enabled: instance.properties2_ext_enabled,
1248            requested_features_chain: criteria.requested_features_chain.clone().into_inner(),
1249            ..Default::default()
1250        };
1251
1252        physical_device.name = physical_device.properties.clone().device_name.to_string();
1253
1254        let available_extensions = unsafe {
1255            instance
1256                .instance
1257                .enumerate_device_extension_properties(vk_phys_device, None)
1258        };
1259
1260        let Ok(available_extensions) = available_extensions else {
1261            return Ok(physical_device);
1262        };
1263
1264        let available_extensions_names = available_extensions
1265            .into_iter()
1266            .map(|e| e.extension_name)
1267            .collect::<BTreeSet<_>>();
1268
1269        physical_device
1270            .available_extensions
1271            .extend(available_extensions_names);
1272
1273        physical_device.properties2_ext_enabled = instance.properties2_ext_enabled;
1274
1275        let requested_features_chain = criteria.requested_features_chain.borrow();
1276        let instance_is_11 = instance.instance_version >= Version::V1_1_0;
1277        if !requested_features_chain.is_empty()
1278            && (instance_is_11 || instance.properties2_ext_enabled)
1279        {
1280            let mut supported_features = requested_features_chain.clone();
1281            let mut local_features = vk::PhysicalDeviceFeatures2::builder();
1282
1283            for node in supported_features.nodes.iter_mut() {
1284                match node {
1285                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(features) => {
1286                        local_features.push_next(features)
1287                    }
1288                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(features) => {
1289                        local_features.push_next(features)
1290                    }
1291                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(features) => {
1292                        local_features.push_next(features)
1293                    }
1294                };
1295            }
1296
1297            unsafe {
1298                instance.instance.get_physical_device_features2(
1299                    physical_device.physical_device,
1300                    &mut local_features,
1301                )
1302            };
1303
1304            physical_device.supported_features_chain = supported_features.clone();
1305        }
1306
1307        Ok(physical_device)
1308    }
1309
1310    fn select_devices(&self) -> crate::Result<BTreeSet<PhysicalDevice>> {
1311        let criteria = &self.selection_criteria;
1312        let instance = self.instance.as_ref();
1313        if criteria.require_present
1314            && !criteria.defer_surface_initialization
1315            && instance.surface.is_none()
1316        {
1317            return Err(crate::PhysicalDeviceError::NoSurfaceProvided.into());
1318        };
1319
1320        let physical_devices = unsafe { instance.instance.enumerate_physical_devices() }
1321            .map_err(|_| crate::PhysicalDeviceError::FailedToEnumeratePhysicalDevices)?;
1322        if physical_devices.is_empty() {
1323            return Err(crate::PhysicalDeviceError::NoPhysicalDevicesFound.into());
1324        };
1325
1326        let fill_out_phys_dev_with_criteria = |physical_device: &mut PhysicalDevice| {
1327            physical_device.features = criteria.required_features;
1328            let mut portability_ext_available = false;
1329            let portability_name = vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name;
1330            for ext in &physical_device.available_extensions {
1331                if criteria.enable_portability_subset && ext == &portability_name {
1332                    portability_ext_available = true;
1333                }
1334            }
1335
1336            physical_device.extensions_to_enable.clear();
1337            physical_device
1338                .extensions_to_enable
1339                .extend(criteria.required_extensions.clone());
1340
1341            if portability_ext_available {
1342                physical_device
1343                    .extensions_to_enable
1344                    .insert(portability_name);
1345            }
1346        };
1347
1348        if criteria.use_first_gpu_unconditionally {
1349            let mut device = self.populate_device_details(physical_devices[0])?;
1350            fill_out_phys_dev_with_criteria(&mut device);
1351            return Ok(BTreeSet::from([device]));
1352        };
1353
1354        let physical_devices = physical_devices
1355            .into_iter()
1356            .filter_map(|p| {
1357                let mut phys_dev = self.populate_device_details(p).ok();
1358
1359                if let Some(phys_dev) = phys_dev.as_mut() {
1360                    self.set_is_suitable(phys_dev);
1361                }
1362
1363                phys_dev.and_then(|mut phys_dev| {
1364                    if phys_dev.suitable == Suitable::No {
1365                        None
1366                    } else {
1367                        fill_out_phys_dev_with_criteria(&mut phys_dev);
1368
1369                        Some(phys_dev)
1370                    }
1371                })
1372            })
1373            .collect::<BTreeSet<_>>();
1374
1375        Ok(physical_devices)
1376    }
1377
1378    /// Select a suitable `PhysicalDevice` according to the configured criteria.
1379    ///
1380    /// Returns a `PhysicalDevice` on success or an error if no suitable device could be found.
1381    pub fn select(self) -> crate::Result<PhysicalDevice> {
1382        let devices = self.select_devices()?;
1383        #[cfg(feature = "enable_tracing")]
1384        {
1385            tracing::debug!(
1386                "Device suitability: {:#?}",
1387                devices
1388                    .iter()
1389                    .map(|d| (&d.name, &d.suitable))
1390                    .collect::<Vec<_>>()
1391            );
1392        }
1393
1394        if devices.is_empty() {
1395            Err(crate::PhysicalDeviceError::NoSuitableDevice.into())
1396        } else {
1397            Ok(unsafe { devices.into_iter().next().unwrap_unchecked() })
1398        }
1399    }
1400}
1401
1402pub struct DeviceBuilder {
1403    instance: Arc<Instance>,
1404    physical_device: PhysicalDevice,
1405    allocation_callbacks: Option<AllocationCallbacks>,
1406    // TODO: pNext chains for features
1407    // TODO: queue descriptions
1408}
1409
1410impl DeviceBuilder {
1411    pub fn new(physical_device: PhysicalDevice, instance: Arc<Instance>) -> DeviceBuilder {
1412        Self {
1413            physical_device,
1414            allocation_callbacks: None,
1415            instance,
1416        }
1417    }
1418
1419    pub fn allocation_callbacks(mut self, allocation_callbacks: AllocationCallbacks) -> Self {
1420        self.allocation_callbacks.replace(allocation_callbacks);
1421        self
1422    }
1423
1424    /// Create a logical `Device` from the configured `PhysicalDevice`.
1425    ///
1426    /// What this does:
1427    /// - Builds queue create infos for each discovered queue family (default priority 1.0).
1428    /// - Enables any device extensions that were marked on the `PhysicalDevice` (and the
1429    ///   `VK_KHR_swapchain` extension when a surface is present or surface init is deferred).
1430    /// - Pushes a `vk::PhysicalDeviceFeatures2` and any requested feature-chain nodes onto the
1431    ///   device create pNext chain when the instance supports properties2 or is Vulkan 1.1+.
1432    /// - Calls `vkCreateDevice` and returns a `Device` wrapper on success.
1433    ///
1434    /// Returns:
1435    /// - `Ok(Device)` containing the created `vulkanalia::Device`, retained `Instance` and
1436    ///   selected `PhysicalDevice` information.
1437    /// - An error if device creation fails.
1438    ///
1439    /// Notes:
1440    /// - Queue configuration is simplified: every queue family discovered by the physical
1441    ///   device is created with a single queue at priority 1.0. Customize if you need
1442    ///   different priorities or explicit queue counts.
1443    /// - Any allocation callbacks previously set via `DeviceBuilder::allocation_callbacks`
1444    ///   are forwarded to `vkCreateDevice` and stored in the returned `Device`.
1445    pub fn build(mut self) -> crate::Result<Device> {
1446        // TODO: custom queue setup
1447        // (index, priorities)
1448        let queue_descriptions = self
1449            .physical_device
1450            .queue_families
1451            .iter()
1452            .enumerate()
1453            .map(|(index, _)| (index, [1.]))
1454            .collect::<Vec<_>>();
1455
1456        let queue_create_infos = queue_descriptions
1457            .iter()
1458            .map(|(index, priorities)| {
1459                vk::DeviceQueueCreateInfo::builder()
1460                    .queue_family_index(*index as u32)
1461                    .queue_priorities(priorities)
1462            })
1463            .collect::<Vec<_>>();
1464
1465        let mut extensions_to_enable = self
1466            .physical_device
1467            .extensions_to_enable
1468            .iter()
1469            .map(|ext| ext.as_ptr())
1470            .collect::<Vec<_>>();
1471
1472        if self.physical_device.surface.is_some()
1473            || self.physical_device.defer_surface_initialization
1474        {
1475            extensions_to_enable.push(vk::KHR_SWAPCHAIN_EXTENSION.name.as_ptr());
1476        }
1477
1478        let mut device_create_info = vk::DeviceCreateInfo::builder()
1479            .queue_create_infos(&queue_create_infos)
1480            .enabled_extension_names(&extensions_to_enable);
1481
1482        let requested_features_chain = &mut self.physical_device.requested_features_chain;
1483
1484        let mut features2 =
1485            vk::PhysicalDeviceFeatures2::builder().features(self.physical_device.features);
1486
1487        if self.instance.instance_version >= Version::V1_1_0
1488            || self.physical_device.properties2_ext_enabled
1489        {
1490            device_create_info = device_create_info.push_next(&mut features2);
1491
1492            for node in requested_features_chain.nodes.iter_mut() {
1493                match node {
1494                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(f) => {
1495                        device_create_info = device_create_info.push_next(f)
1496                    }
1497                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(f) => {
1498                        device_create_info = device_create_info.push_next(f)
1499                    }
1500                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(f) => {
1501                        device_create_info = device_create_info.push_next(f)
1502                    }
1503                }
1504            }
1505        }
1506
1507        let device = unsafe {
1508            self.instance.instance.create_device(
1509                self.physical_device.physical_device,
1510                &device_create_info,
1511                self.allocation_callbacks.as_ref(),
1512            )
1513        }?;
1514
1515        let instance = self.instance;
1516        let physical_device = self.physical_device;
1517
1518        let surface = physical_device.surface;
1519        let allocation_callbacks = self.allocation_callbacks;
1520
1521        Ok(Device {
1522            instance,
1523            device,
1524            surface,
1525            physical_device,
1526            allocation_callbacks,
1527        })
1528    }
1529}
1530
1531#[derive(Debug)]
1532pub struct Device {
1533    instance: Arc<Instance>,
1534    device: vulkanalia::Device,
1535    physical_device: PhysicalDevice,
1536    surface: Option<vk::SurfaceKHR>,
1537    allocation_callbacks: Option<AllocationCallbacks>,
1538}
1539
1540#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1541pub enum QueueType {
1542    Present,
1543    Graphics,
1544    Compute,
1545    Transfer,
1546}
1547
1548impl Device {
1549    pub fn device(&self) -> &vulkanalia::Device {
1550        &self.device
1551    }
1552
1553    pub fn physical_device(&self) -> &PhysicalDevice {
1554        &self.physical_device
1555    }
1556
1557    pub fn get_queue(&self, queue: QueueType) -> crate::Result<(usize, vk::Queue)> {
1558        let index = match queue {
1559            QueueType::Present => get_present_queue_index(
1560                &self.instance.instance,
1561                self.physical_device.physical_device,
1562                self.surface,
1563                &self.physical_device.queue_families,
1564            )
1565            .ok_or(crate::QueueError::PresentUnavailable),
1566            QueueType::Graphics => get_first_queue_index(
1567                &self.physical_device.queue_families,
1568                vk::QueueFlags::GRAPHICS,
1569            )
1570            .ok_or(crate::QueueError::GraphicsUnavailable),
1571            QueueType::Compute => get_separate_queue_index(
1572                &self.physical_device.queue_families,
1573                vk::QueueFlags::COMPUTE,
1574                vk::QueueFlags::TRANSFER,
1575            )
1576            .ok_or(crate::QueueError::ComputeUnavailable),
1577            QueueType::Transfer => get_separate_queue_index(
1578                &self.physical_device.queue_families,
1579                vk::QueueFlags::TRANSFER,
1580                vk::QueueFlags::COMPUTE,
1581            )
1582            .ok_or(crate::QueueError::TransferUnavailable),
1583        }?;
1584
1585        Ok((index, unsafe {
1586            self.device.get_device_queue(index as _, 0)
1587        }))
1588    }
1589
1590    pub fn get_dedicated_queue(&self, queue: QueueType) -> crate::Result<vk::Queue> {
1591        let index = match queue {
1592            QueueType::Compute => get_dedicated_queue_index(
1593                &self.physical_device.queue_families,
1594                vk::QueueFlags::COMPUTE,
1595                vk::QueueFlags::TRANSFER,
1596            )
1597            .ok_or(crate::QueueError::ComputeUnavailable),
1598            QueueType::Transfer => get_dedicated_queue_index(
1599                &self.physical_device.queue_families,
1600                vk::QueueFlags::TRANSFER,
1601                vk::QueueFlags::COMPUTE,
1602            )
1603            .ok_or(crate::QueueError::TransferUnavailable),
1604            _ => return Err(crate::QueueError::InvalidQueueFamilyIndex.into()),
1605        }?;
1606
1607        let info = vk::DeviceQueueInfo2::builder()
1608            .queue_family_index(index as _)
1609            .queue_index(0);
1610
1611        Ok(unsafe { self.device.get_device_queue2(&info) })
1612    }
1613
1614    pub fn destroy(&self) {
1615        unsafe {
1616            self.device
1617                .destroy_device(self.allocation_callbacks.as_ref());
1618        }
1619    }
1620}
1621
1622impl AsRef<vulkanalia::Device> for Device {
1623    fn as_ref(&self) -> &vulkanalia::Device {
1624        &self.device
1625    }
1626}
1627
1628impl Deref for Device {
1629    type Target = vulkanalia::Device;
1630
1631    fn deref(&self) -> &Self::Target {
1632        &self.device
1633    }
1634}