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
313#[derive(Debug, Clone)]
314pub enum VulkanPhysicalDeviceFeature2 {
315    PhysicalDeviceVulkan11(vk::PhysicalDeviceVulkan11Features),
316    PhysicalDeviceVulkan12(vk::PhysicalDeviceVulkan12Features),
317    PhysicalDeviceVulkan13(vk::PhysicalDeviceVulkan13Features),
318}
319
320fn match_features(
321    requested: &VulkanPhysicalDeviceFeature2,
322    supported: &VulkanPhysicalDeviceFeature2,
323) -> bool {
324    assert_eq!(requested.s_type(), supported.s_type());
325
326    match (requested, supported) {
327        (
328            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(r),
329            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(s),
330        ) => {
331            if r.storage_buffer_16bit_access == vk::TRUE
332                && s.storage_buffer_16bit_access == vk::FALSE
333            {
334                return false;
335            }
336            if r.uniform_and_storage_buffer_16bit_access == vk::TRUE
337                && s.uniform_and_storage_buffer_16bit_access == vk::FALSE
338            {
339                return false;
340            }
341            if r.storage_push_constant16 == vk::TRUE && s.storage_push_constant16 == vk::FALSE {
342                return false;
343            }
344            if r.storage_input_output16 == vk::TRUE && s.storage_input_output16 == vk::FALSE {
345                return false;
346            }
347            if r.multiview == vk::TRUE && s.multiview == vk::FALSE {
348                return false;
349            }
350            if r.multiview_geometry_shader == vk::TRUE && s.multiview_geometry_shader == vk::FALSE {
351                return false;
352            }
353            if r.multiview_tessellation_shader == vk::TRUE
354                && s.multiview_tessellation_shader == vk::FALSE
355            {
356                return false;
357            }
358            if r.variable_pointers_storage_buffer == vk::TRUE
359                && s.variable_pointers_storage_buffer == vk::FALSE
360            {
361                return false;
362            }
363            if r.variable_pointers == vk::TRUE && s.variable_pointers == vk::FALSE {
364                return false;
365            }
366            if r.protected_memory == vk::TRUE && s.protected_memory == vk::FALSE {
367                return false;
368            }
369            if r.sampler_ycbcr_conversion == vk::TRUE && s.sampler_ycbcr_conversion == vk::FALSE {
370                return false;
371            }
372            if r.shader_draw_parameters == vk::TRUE && s.shader_draw_parameters == vk::FALSE {
373                return false;
374            }
375            true
376        }
377        (
378            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(r),
379            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(s),
380        ) => {
381            if r.sampler_mirror_clamp_to_edge == vk::TRUE
382                && s.sampler_mirror_clamp_to_edge == vk::FALSE
383            {
384                return false;
385            }
386            if r.draw_indirect_count == vk::TRUE && s.draw_indirect_count == vk::FALSE {
387                return false;
388            }
389            if r.storage_buffer_8bit_access == vk::TRUE && s.storage_buffer_8bit_access == vk::FALSE
390            {
391                return false;
392            }
393            if r.uniform_and_storage_buffer_8bit_access == vk::TRUE
394                && s.uniform_and_storage_buffer_8bit_access == vk::FALSE
395            {
396                return false;
397            }
398            if r.storage_push_constant8 == vk::TRUE && s.storage_push_constant8 == vk::FALSE {
399                return false;
400            }
401            if r.shader_buffer_int64_atomics == vk::TRUE
402                && s.shader_buffer_int64_atomics == vk::FALSE
403            {
404                return false;
405            }
406            if r.shader_shared_int64_atomics == vk::TRUE
407                && s.shader_shared_int64_atomics == vk::FALSE
408            {
409                return false;
410            }
411            if r.shader_float16 == vk::TRUE && s.shader_float16 == vk::FALSE {
412                return false;
413            }
414            if r.shader_int8 == vk::TRUE && s.shader_int8 == vk::FALSE {
415                return false;
416            }
417            if r.descriptor_indexing == vk::TRUE && s.descriptor_indexing == vk::FALSE {
418                return false;
419            }
420            if r.shader_input_attachment_array_dynamic_indexing == vk::TRUE
421                && s.shader_input_attachment_array_dynamic_indexing == vk::FALSE
422            {
423                return false;
424            }
425            if r.shader_uniform_texel_buffer_array_dynamic_indexing == vk::TRUE
426                && s.shader_uniform_texel_buffer_array_dynamic_indexing == vk::FALSE
427            {
428                return false;
429            }
430            if r.shader_storage_texel_buffer_array_dynamic_indexing == vk::TRUE
431                && s.shader_storage_texel_buffer_array_dynamic_indexing == vk::FALSE
432            {
433                return false;
434            }
435            if r.shader_uniform_buffer_array_non_uniform_indexing == vk::TRUE
436                && s.shader_uniform_buffer_array_non_uniform_indexing == vk::FALSE
437            {
438                return false;
439            }
440            if r.shader_sampled_image_array_non_uniform_indexing == vk::TRUE
441                && s.shader_sampled_image_array_non_uniform_indexing == vk::FALSE
442            {
443                return false;
444            }
445            if r.shader_storage_buffer_array_non_uniform_indexing == vk::TRUE
446                && s.shader_storage_buffer_array_non_uniform_indexing == vk::FALSE
447            {
448                return false;
449            }
450            if r.shader_storage_image_array_non_uniform_indexing == vk::TRUE
451                && s.shader_storage_image_array_non_uniform_indexing == vk::FALSE
452            {
453                return false;
454            }
455            if r.shader_input_attachment_array_non_uniform_indexing == vk::TRUE
456                && s.shader_input_attachment_array_non_uniform_indexing == vk::FALSE
457            {
458                return false;
459            }
460            if r.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::TRUE
461                && s.shader_uniform_texel_buffer_array_non_uniform_indexing == vk::FALSE
462            {
463                return false;
464            }
465            if r.shader_storage_texel_buffer_array_non_uniform_indexing == vk::TRUE
466                && s.shader_storage_texel_buffer_array_non_uniform_indexing == vk::FALSE
467            {
468                return false;
469            }
470            if r.descriptor_binding_uniform_buffer_update_after_bind == vk::TRUE
471                && s.descriptor_binding_uniform_buffer_update_after_bind == vk::FALSE
472            {
473                return false;
474            }
475            if r.descriptor_binding_sampled_image_update_after_bind == vk::TRUE
476                && s.descriptor_binding_sampled_image_update_after_bind == vk::FALSE
477            {
478                return false;
479            }
480            if r.descriptor_binding_storage_image_update_after_bind == vk::TRUE
481                && s.descriptor_binding_storage_image_update_after_bind == vk::FALSE
482            {
483                return false;
484            }
485            if r.descriptor_binding_storage_buffer_update_after_bind == vk::TRUE
486                && s.descriptor_binding_storage_buffer_update_after_bind == vk::FALSE
487            {
488                return false;
489            }
490            if r.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::TRUE
491                && s.descriptor_binding_uniform_texel_buffer_update_after_bind == vk::FALSE
492            {
493                return false;
494            }
495            if r.descriptor_binding_storage_texel_buffer_update_after_bind == vk::TRUE
496                && s.descriptor_binding_storage_texel_buffer_update_after_bind == vk::FALSE
497            {
498                return false;
499            }
500            if r.descriptor_binding_update_unused_while_pending == vk::TRUE
501                && s.descriptor_binding_update_unused_while_pending == vk::FALSE
502            {
503                return false;
504            }
505            if r.descriptor_binding_partially_bound == vk::TRUE
506                && s.descriptor_binding_partially_bound == vk::FALSE
507            {
508                return false;
509            }
510            if r.descriptor_binding_variable_descriptor_count == vk::TRUE
511                && s.descriptor_binding_variable_descriptor_count == vk::FALSE
512            {
513                return false;
514            }
515            if r.runtime_descriptor_array == vk::TRUE && s.runtime_descriptor_array == vk::FALSE {
516                return false;
517            }
518            if r.sampler_filter_minmax == vk::TRUE && s.sampler_filter_minmax == vk::FALSE {
519                return false;
520            }
521            if r.scalar_block_layout == vk::TRUE && s.scalar_block_layout == vk::FALSE {
522                return false;
523            }
524            if r.imageless_framebuffer == vk::TRUE && s.imageless_framebuffer == vk::FALSE {
525                return false;
526            }
527            if r.uniform_buffer_standard_layout == vk::TRUE
528                && s.uniform_buffer_standard_layout == vk::FALSE
529            {
530                return false;
531            }
532            if r.shader_subgroup_extended_types == vk::TRUE
533                && s.shader_subgroup_extended_types == vk::FALSE
534            {
535                return false;
536            }
537            if r.separate_depth_stencil_layouts == vk::TRUE
538                && s.separate_depth_stencil_layouts == vk::FALSE
539            {
540                return false;
541            }
542            if r.host_query_reset == vk::TRUE && s.host_query_reset == vk::FALSE {
543                return false;
544            }
545            if r.timeline_semaphore == vk::TRUE && s.timeline_semaphore == vk::FALSE {
546                return false;
547            }
548            if r.buffer_device_address == vk::TRUE && s.buffer_device_address == vk::FALSE {
549                return false;
550            }
551            if r.buffer_device_address_capture_replay == vk::TRUE
552                && s.buffer_device_address_capture_replay == vk::FALSE
553            {
554                return false;
555            }
556            if r.buffer_device_address_multi_device == vk::TRUE
557                && s.buffer_device_address_multi_device == vk::FALSE
558            {
559                return false;
560            }
561            if r.vulkan_memory_model == vk::TRUE && s.vulkan_memory_model == vk::FALSE {
562                return false;
563            }
564            if r.vulkan_memory_model_device_scope == vk::TRUE
565                && s.vulkan_memory_model_device_scope == vk::FALSE
566            {
567                return false;
568            }
569            if r.vulkan_memory_model_availability_visibility_chains == vk::TRUE
570                && s.vulkan_memory_model_availability_visibility_chains == vk::FALSE
571            {
572                return false;
573            }
574            if r.shader_output_viewport_index == vk::TRUE
575                && s.shader_output_viewport_index == vk::FALSE
576            {
577                return false;
578            }
579            if r.shader_output_layer == vk::TRUE && s.shader_output_layer == vk::FALSE {
580                return false;
581            }
582            if r.subgroup_broadcast_dynamic_id == vk::TRUE
583                && s.subgroup_broadcast_dynamic_id == vk::FALSE
584            {
585                return false;
586            }
587            true
588        }
589        (
590            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(r),
591            VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(s),
592        ) => {
593            if r.robust_image_access == vk::TRUE && s.robust_image_access == vk::FALSE {
594                return false;
595            }
596            if r.inline_uniform_block == vk::TRUE && s.inline_uniform_block == vk::FALSE {
597                return false;
598            }
599            if r.descriptor_binding_inline_uniform_block_update_after_bind == vk::TRUE
600                && s.descriptor_binding_inline_uniform_block_update_after_bind == vk::FALSE
601            {
602                return false;
603            }
604            if r.pipeline_creation_cache_control == vk::TRUE
605                && s.pipeline_creation_cache_control == vk::FALSE
606            {
607                return false;
608            }
609            if r.private_data == vk::TRUE && s.private_data == vk::FALSE {
610                return false;
611            }
612            if r.shader_demote_to_helper_invocation == vk::TRUE
613                && s.shader_demote_to_helper_invocation == vk::FALSE
614            {
615                return false;
616            }
617            if r.shader_terminate_invocation == vk::TRUE
618                && s.shader_terminate_invocation == vk::FALSE
619            {
620                return false;
621            }
622            if r.subgroup_size_control == vk::TRUE && s.subgroup_size_control == vk::FALSE {
623                return false;
624            }
625            if r.compute_full_subgroups == vk::TRUE && s.compute_full_subgroups == vk::FALSE {
626                return false;
627            }
628            if r.synchronization2 == vk::TRUE && s.synchronization2 == vk::FALSE {
629                return false;
630            }
631            if r.texture_compression_astc_hdr == vk::TRUE
632                && s.texture_compression_astc_hdr == vk::FALSE
633            {
634                return false;
635            }
636            if r.shader_zero_initialize_workgroup_memory == vk::TRUE
637                && s.shader_zero_initialize_workgroup_memory == vk::FALSE
638            {
639                return false;
640            }
641            if r.dynamic_rendering == vk::TRUE && s.dynamic_rendering == vk::FALSE {
642                return false;
643            }
644            if r.shader_integer_dot_product == vk::TRUE && s.shader_integer_dot_product == vk::FALSE
645            {
646                return false;
647            }
648            if r.maintenance4 == vk::TRUE && s.maintenance4 == vk::FALSE {
649                return false;
650            }
651            true
652        }
653        _ => unsafe { unreachable_unchecked() },
654    }
655}
656impl<'a> VulkanPhysicalDeviceFeature2 {
657    fn combine(&mut self, other: &VulkanPhysicalDeviceFeature2) {
658        assert_eq!(self.s_type(), other.s_type());
659
660        match (self, other) {
661            (
662                Self::PhysicalDeviceVulkan11(f),
663                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(other),
664            ) => {
665                f.storage_buffer_16bit_access |= other.storage_buffer_16bit_access;
666                f.uniform_and_storage_buffer_16bit_access |=
667                    other.uniform_and_storage_buffer_16bit_access;
668                f.storage_push_constant16 |= other.storage_push_constant16;
669                f.storage_input_output16 |= other.storage_input_output16;
670                f.multiview |= other.multiview;
671                f.multiview_geometry_shader |= other.multiview_geometry_shader;
672                f.multiview_tessellation_shader |= other.multiview_tessellation_shader;
673                f.variable_pointers_storage_buffer |= other.variable_pointers_storage_buffer;
674                f.variable_pointers |= other.variable_pointers;
675                f.protected_memory |= other.protected_memory;
676                f.sampler_ycbcr_conversion |= other.sampler_ycbcr_conversion;
677                f.shader_draw_parameters |= other.shader_draw_parameters;
678            }
679            (
680                Self::PhysicalDeviceVulkan12(f),
681                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(other),
682            ) => {
683                f.sampler_mirror_clamp_to_edge |= other.sampler_mirror_clamp_to_edge;
684                f.draw_indirect_count |= other.draw_indirect_count;
685                f.storage_buffer_8bit_access |= other.storage_buffer_8bit_access;
686                f.uniform_and_storage_buffer_8bit_access |=
687                    other.uniform_and_storage_buffer_8bit_access;
688                f.storage_push_constant8 |= other.storage_push_constant8;
689                f.shader_buffer_int64_atomics |= other.shader_buffer_int64_atomics;
690                f.shader_shared_int64_atomics |= other.shader_shared_int64_atomics;
691                f.shader_float16 |= other.shader_float16;
692                f.shader_int8 |= other.shader_int8;
693                f.descriptor_indexing |= other.descriptor_indexing;
694                f.shader_input_attachment_array_dynamic_indexing |=
695                    other.shader_input_attachment_array_dynamic_indexing;
696                f.shader_uniform_texel_buffer_array_dynamic_indexing |=
697                    other.shader_uniform_texel_buffer_array_dynamic_indexing;
698                f.shader_storage_texel_buffer_array_dynamic_indexing |=
699                    other.shader_storage_texel_buffer_array_dynamic_indexing;
700                f.shader_uniform_buffer_array_non_uniform_indexing |=
701                    other.shader_uniform_buffer_array_non_uniform_indexing;
702                f.shader_sampled_image_array_non_uniform_indexing |=
703                    other.shader_sampled_image_array_non_uniform_indexing;
704                f.shader_storage_buffer_array_non_uniform_indexing |=
705                    other.shader_storage_buffer_array_non_uniform_indexing;
706                f.shader_storage_image_array_non_uniform_indexing |=
707                    other.shader_storage_image_array_non_uniform_indexing;
708                f.shader_input_attachment_array_non_uniform_indexing |=
709                    other.shader_input_attachment_array_non_uniform_indexing;
710                f.shader_uniform_texel_buffer_array_non_uniform_indexing |=
711                    other.shader_uniform_texel_buffer_array_non_uniform_indexing;
712                f.shader_storage_texel_buffer_array_non_uniform_indexing |=
713                    other.shader_storage_texel_buffer_array_non_uniform_indexing;
714                f.descriptor_binding_uniform_buffer_update_after_bind |=
715                    other.descriptor_binding_uniform_buffer_update_after_bind;
716                f.descriptor_binding_sampled_image_update_after_bind |=
717                    other.descriptor_binding_sampled_image_update_after_bind;
718                f.descriptor_binding_storage_image_update_after_bind |=
719                    other.descriptor_binding_storage_image_update_after_bind;
720                f.descriptor_binding_storage_buffer_update_after_bind |=
721                    other.descriptor_binding_storage_buffer_update_after_bind;
722                f.descriptor_binding_uniform_texel_buffer_update_after_bind |=
723                    other.descriptor_binding_uniform_texel_buffer_update_after_bind;
724                f.descriptor_binding_storage_texel_buffer_update_after_bind |=
725                    other.descriptor_binding_storage_texel_buffer_update_after_bind;
726                f.descriptor_binding_update_unused_while_pending |=
727                    other.descriptor_binding_update_unused_while_pending;
728                f.descriptor_binding_partially_bound |= other.descriptor_binding_partially_bound;
729                f.descriptor_binding_variable_descriptor_count |=
730                    other.descriptor_binding_variable_descriptor_count;
731                f.runtime_descriptor_array |= other.runtime_descriptor_array;
732                f.sampler_filter_minmax |= other.sampler_filter_minmax;
733                f.scalar_block_layout |= other.scalar_block_layout;
734                f.imageless_framebuffer |= other.imageless_framebuffer;
735                f.uniform_buffer_standard_layout |= other.uniform_buffer_standard_layout;
736                f.shader_subgroup_extended_types |= other.shader_subgroup_extended_types;
737                f.separate_depth_stencil_layouts |= other.separate_depth_stencil_layouts;
738                f.host_query_reset |= other.host_query_reset;
739                f.timeline_semaphore |= other.timeline_semaphore;
740                f.buffer_device_address |= other.buffer_device_address;
741                f.buffer_device_address_capture_replay |=
742                    other.buffer_device_address_capture_replay;
743                f.buffer_device_address_multi_device |= other.buffer_device_address_multi_device;
744                f.vulkan_memory_model |= other.vulkan_memory_model;
745                f.vulkan_memory_model_device_scope |= other.vulkan_memory_model_device_scope;
746                f.vulkan_memory_model_availability_visibility_chains |=
747                    other.vulkan_memory_model_availability_visibility_chains;
748                f.shader_output_viewport_index |= other.shader_output_viewport_index;
749                f.shader_output_layer |= other.shader_output_layer;
750                f.subgroup_broadcast_dynamic_id |= other.subgroup_broadcast_dynamic_id;
751            }
752            (
753                Self::PhysicalDeviceVulkan13(f),
754                VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(other),
755            ) => {
756                f.robust_image_access |= other.robust_image_access;
757                f.inline_uniform_block |= other.inline_uniform_block;
758                f.descriptor_binding_inline_uniform_block_update_after_bind |=
759                    other.descriptor_binding_inline_uniform_block_update_after_bind;
760                f.pipeline_creation_cache_control |= other.pipeline_creation_cache_control;
761                f.private_data |= other.private_data;
762                f.shader_demote_to_helper_invocation |= other.shader_demote_to_helper_invocation;
763                f.shader_terminate_invocation |= other.shader_terminate_invocation;
764                f.subgroup_size_control |= other.subgroup_size_control;
765                f.compute_full_subgroups |= other.compute_full_subgroups;
766                f.synchronization2 |= other.synchronization2;
767                f.texture_compression_astc_hdr |= other.texture_compression_astc_hdr;
768                f.shader_zero_initialize_workgroup_memory |=
769                    other.shader_zero_initialize_workgroup_memory;
770                f.dynamic_rendering |= other.dynamic_rendering;
771                f.shader_integer_dot_product |= other.shader_integer_dot_product;
772                f.maintenance4 |= other.maintenance4;
773            }
774            _ => unsafe { unreachable_unchecked() },
775        }
776    }
777
778    fn s_type(&self) -> vk::StructureType {
779        match self {
780            Self::PhysicalDeviceVulkan11(f) => f.s_type,
781            Self::PhysicalDeviceVulkan12(f) => f.s_type,
782            Self::PhysicalDeviceVulkan13(f) => f.s_type,
783        }
784    }
785}
786
787impl From<vk::PhysicalDeviceVulkan11Features> for VulkanPhysicalDeviceFeature2 {
788    fn from(value: vk::PhysicalDeviceVulkan11Features) -> Self {
789        Self::PhysicalDeviceVulkan11(value)
790    }
791}
792
793impl From<vk::PhysicalDeviceVulkan12Features> for VulkanPhysicalDeviceFeature2 {
794    fn from(value: vk::PhysicalDeviceVulkan12Features) -> Self {
795        Self::PhysicalDeviceVulkan12(value)
796    }
797}
798
799impl From<vk::PhysicalDeviceVulkan13Features> for VulkanPhysicalDeviceFeature2 {
800    fn from(value: vk::PhysicalDeviceVulkan13Features) -> Self {
801        Self::PhysicalDeviceVulkan13(value)
802    }
803}
804//endregion vulkanfeatures
805
806#[derive(Debug, Clone, Default)]
807struct GenericFeatureChain {
808    nodes: Vec<VulkanPhysicalDeviceFeature2>,
809}
810
811impl Deref for GenericFeatureChain {
812    type Target = Vec<VulkanPhysicalDeviceFeature2>;
813
814    fn deref(&self) -> &Self::Target {
815        &self.nodes
816    }
817}
818
819impl GenericFeatureChain {
820    fn new() -> Self {
821        Self { nodes: vec![] }
822    }
823
824    fn add(&mut self, feature: impl Into<VulkanPhysicalDeviceFeature2>) {
825        let new_node = feature.into();
826
827        for node in &mut self.nodes {
828            if new_node.s_type() == node.s_type() {
829                node.combine(&new_node);
830                return;
831            }
832        }
833
834        self.nodes.push(new_node);
835    }
836
837    fn match_all(&self, features_requested: &GenericFeatureChain) -> bool {
838        if features_requested.nodes.len() != self.nodes.len() {
839            return false;
840        }
841
842        let features_requested = features_requested.nodes.as_slice();
843        let features = self.nodes.as_slice();
844
845        for (requested_node, node) in features_requested.iter().zip(features) {
846            if !match_features(requested_node, node) {
847                return false;
848            }
849        }
850
851        true
852    }
853}
854
855#[derive(Debug)]
856struct SelectionCriteria {
857    name: String,
858    preferred_device_type: PreferredDeviceType,
859    allow_any_type: bool,
860    require_present: bool,
861    require_dedicated_transfer_queue: bool,
862    require_dedicated_compute_queue: bool,
863    require_separate_transfer_queue: bool,
864    require_separate_compute_queue: bool,
865    required_mem_size: vk::DeviceSize,
866    required_extensions: BTreeSet<vk::ExtensionName>,
867    required_version: Version,
868    required_features: vk::PhysicalDeviceFeatures,
869    required_formats: Vec<vk::Format>,
870    requested_features_chain: RefCell<GenericFeatureChain>,
871    defer_surface_initialization: bool,
872    use_first_gpu_unconditionally: bool,
873    enable_portability_subset: bool,
874}
875
876impl Default for SelectionCriteria {
877    fn default() -> Self {
878        Self {
879            name: String::new(),
880            preferred_device_type: PreferredDeviceType::Discrete,
881            allow_any_type: true,
882            require_present: true,
883            require_dedicated_transfer_queue: false,
884            require_dedicated_compute_queue: false,
885            require_separate_transfer_queue: false,
886            require_separate_compute_queue: false,
887            required_mem_size: 0,
888            required_extensions: BTreeSet::new(),
889            required_version: Version::V1_0_0,
890            required_features: vk::PhysicalDeviceFeatures::default(),
891            defer_surface_initialization: false,
892            use_first_gpu_unconditionally: false,
893            enable_portability_subset: true,
894            requested_features_chain: RefCell::new(GenericFeatureChain::new()),
895            required_formats: vec![],
896        }
897    }
898}
899
900pub struct PhysicalDeviceSelector {
901    instance: Arc<Instance>,
902    surface: Option<vk::SurfaceKHR>,
903    selection_criteria: SelectionCriteria,
904}
905
906impl PhysicalDeviceSelector {
907    /// Create a new `PhysicalDeviceSelector` for the provided `Instance`.
908    ///
909    /// The selector can be configured with builder-style methods before calling `select`.
910    pub fn new(instance: Arc<Instance>) -> PhysicalDeviceSelector {
911        let enable_portability_subset = cfg!(feature = "portability");
912        let require_present = instance.surface.is_some();
913        let required_version = instance.api_version;
914        Self {
915            surface: instance.surface,
916            instance,
917            selection_criteria: SelectionCriteria {
918                require_present,
919                required_version,
920                enable_portability_subset,
921                ..Default::default()
922            },
923        }
924    }
925
926    /// Specify a surface to use when evaluating device presentation support.
927    pub fn surface(mut self, surface: vk::SurfaceKHR) -> Self {
928        self.surface.replace(surface);
929        self
930    }
931
932    /// Add an additional device feature (vulkan feature2 struct) that must be supported by
933    /// the physical device in order to be selected.
934    pub fn add_required_extension_feature<T: Into<VulkanPhysicalDeviceFeature2>>(
935        self,
936        feature: T,
937    ) -> Self {
938        self.selection_criteria
939            .requested_features_chain
940            .borrow_mut()
941            .add(feature);
942        self
943    }
944
945    /// Require the given `vk::PhysicalDeviceFeatures` when selecting a physical device.
946    pub fn add_required_features(mut self, features: vk::PhysicalDeviceFeatures) -> Self {
947        self.selection_criteria.required_features = features;
948        self
949    }
950
951    /// Restrict selection to devices whose name matches `name`.
952    pub fn name(mut self, name: impl Into<String>) -> Self {
953        self.selection_criteria.name = name.into();
954        self
955    }
956
957    /// Prefer devices of the given `PreferredDeviceType` when ranking candidates.
958    pub fn preferred_device_type(mut self, device_type: PreferredDeviceType) -> Self {
959        self.selection_criteria.preferred_device_type = device_type;
960        self
961    }
962
963    /// Allow devices of any GPU type (when true) or restrict to the preferred device type.
964    pub fn allow_any_gpu_device_type(mut self, allow: bool) -> Self {
965        self.selection_criteria.allow_any_type = allow;
966        self
967    }
968
969    /// Require a dedicated transfer-only queue family to be present on the physical device.
970    pub fn require_dedicated_transfer_queue(mut self, require: bool) -> Self {
971        self.selection_criteria.require_dedicated_transfer_queue = require;
972        self
973    }
974
975    /// Require a dedicated compute-only queue family to be present on the physical device.
976    pub fn require_dedicated_compute_queue(mut self, require: bool) -> Self {
977        self.selection_criteria.require_dedicated_compute_queue = require;
978        self
979    }
980
981    /// Require a queue family separate from graphics for transfer operations.
982    pub fn require_separate_transfer_queue(mut self, require: bool) -> Self {
983        self.selection_criteria.require_separate_transfer_queue = require;
984        self
985    }
986
987    /// Require a queue family separate from graphics for compute operations.
988    pub fn require_separate_compute_queue(mut self, require: bool) -> Self {
989        self.selection_criteria.require_separate_compute_queue = require;
990        self
991    }
992
993    /// Require the device to have at least `required` bytes of device-local memory.
994    pub fn required_device_memory_size(mut self, required: vk::DeviceSize) -> Self {
995        self.selection_criteria.required_mem_size = required;
996        self
997    }
998
999    /// Require support for the provided list of `vk::Format`s on the device's surface.
1000    pub fn required_formats(mut self, required: impl IntoIterator<Item = vk::Format>) -> Self {
1001        self.selection_criteria.required_formats = required.into_iter().collect();
1002        self
1003    }
1004
1005    /// If `select` is true, automatically select the first enumerated physical device
1006    /// without applying suitability checks.
1007    pub fn select_first_device_unconditionally(mut self, select: bool) -> Self {
1008        self.selection_criteria.use_first_gpu_unconditionally = select;
1009        self
1010    }
1011
1012    fn set_is_suitable(&self, device: &mut PhysicalDevice) {
1013        let criteria = &self.selection_criteria;
1014
1015        let device_name = device.properties.device_name.to_string_lossy();
1016
1017        if !criteria.name.is_empty() && Cow::Borrowed(&criteria.name) != device_name {
1018            #[cfg(feature = "enable_tracing")]
1019            {
1020                tracing::warn!(
1021                    "Device {} is not suitable. Name requested: {}",
1022                    device_name,
1023                    criteria.name
1024                );
1025            }
1026            device.suitable = Suitable::No;
1027            return;
1028        };
1029
1030        if u32::from(criteria.required_version) > device.properties.api_version {
1031            #[cfg(feature = "enable_tracing")]
1032            {
1033                let requested_version = criteria.required_version;
1034                let available_version = device.properties.api_version;
1035                tracing::warn!(
1036                    "Device {} is not suitable. Requested version: {}, Available version: {}",
1037                    device_name,
1038                    requested_version,
1039                    available_version
1040                );
1041            }
1042            device.suitable = Suitable::No;
1043            return;
1044        }
1045
1046        let dedicated_compute = get_dedicated_queue_index(
1047            &device.queue_families,
1048            vk::QueueFlags::COMPUTE,
1049            vk::QueueFlags::TRANSFER,
1050        );
1051
1052        let dedicated_transfer = get_dedicated_queue_index(
1053            &device.queue_families,
1054            vk::QueueFlags::TRANSFER,
1055            vk::QueueFlags::COMPUTE,
1056        );
1057
1058        let separate_compute = get_separate_queue_index(
1059            &device.queue_families,
1060            vk::QueueFlags::COMPUTE,
1061            vk::QueueFlags::TRANSFER,
1062        );
1063
1064        let separate_transfer = get_separate_queue_index(
1065            &device.queue_families,
1066            vk::QueueFlags::TRANSFER,
1067            vk::QueueFlags::COMPUTE,
1068        );
1069
1070        let present_queue = get_present_queue_index(
1071            &self.instance.instance,
1072            device.physical_device,
1073            self.surface,
1074            &device.queue_families,
1075        );
1076
1077        if criteria.require_dedicated_compute_queue && dedicated_compute.is_none() {
1078            device.suitable = Suitable::No;
1079            return;
1080        }
1081
1082        if criteria.require_dedicated_transfer_queue && dedicated_transfer.is_none() {
1083            device.suitable = Suitable::No;
1084            return;
1085        }
1086
1087        if criteria.require_separate_transfer_queue && separate_transfer.is_none() {
1088            device.suitable = Suitable::No;
1089            return;
1090        }
1091
1092        if criteria.require_separate_compute_queue && separate_compute.is_none() {
1093            device.suitable = Suitable::No;
1094            return;
1095        }
1096
1097        if criteria.require_present
1098            && present_queue.is_none()
1099            && !criteria.defer_surface_initialization
1100        {
1101            device.suitable = Suitable::No;
1102            return;
1103        }
1104
1105        let required_extensions_supported = check_device_extension_support(
1106            &device.available_extensions,
1107            &criteria.required_extensions,
1108        );
1109
1110        if required_extensions_supported.len() != criteria.required_extensions.len() {
1111            device.suitable = Suitable::No;
1112            return;
1113        }
1114
1115        if !criteria.defer_surface_initialization && criteria.require_present {
1116            if let Some(surface) = self.surface {
1117                let formats = unsafe {
1118                    self.instance
1119                        .instance
1120                        .get_physical_device_surface_formats_khr(device.physical_device, surface)
1121                };
1122                let Ok(formats) = formats else {
1123                    device.suitable = Suitable::No;
1124                    return;
1125                };
1126
1127                let present_modes = unsafe {
1128                    self.instance
1129                        .instance
1130                        .get_physical_device_surface_present_modes_khr(
1131                            device.physical_device,
1132                            surface,
1133                        )
1134                };
1135                let Ok(present_modes) = present_modes else {
1136                    device.suitable = Suitable::No;
1137                    return;
1138                };
1139
1140                if present_modes.is_empty() || formats.is_empty() {
1141                    device.suitable = Suitable::No;
1142                    return;
1143                }
1144            };
1145        };
1146
1147        let preferred_device_type =
1148            vk::PhysicalDeviceType::from_raw(criteria.preferred_device_type as u8 as i32);
1149        if !criteria.allow_any_type && device.properties.device_type != preferred_device_type {
1150            device.suitable = Suitable::Partial;
1151        }
1152
1153        let required_features_supported = supports_features(
1154            &device.features,
1155            &criteria.required_features,
1156            &device.supported_features_chain,
1157            &criteria.requested_features_chain.borrow(),
1158        );
1159
1160        if !required_features_supported {
1161            device.suitable = Suitable::No;
1162            return;
1163        }
1164
1165        //let supported_formats = &device.format_properties;
1166
1167        for memory_heap in device.memory_properties.memory_heaps {
1168            if memory_heap
1169                .flags
1170                .contains(vk::MemoryHeapFlags::DEVICE_LOCAL)
1171                && memory_heap.size < criteria.required_mem_size
1172            {
1173                device.suitable = Suitable::No;
1174                return;
1175            }
1176        }
1177    }
1178
1179    fn populate_device_details(
1180        &self,
1181        vk_phys_device: vk::PhysicalDevice,
1182    ) -> crate::Result<PhysicalDevice> {
1183        let instance = self.instance.as_ref();
1184        let criteria = &self.selection_criteria;
1185
1186        let mut physical_device = PhysicalDevice {
1187            physical_device: vk_phys_device,
1188            surface: instance.surface,
1189            defer_surface_initialization: criteria.defer_surface_initialization,
1190            queue_families: unsafe {
1191                instance
1192                    .instance
1193                    .get_physical_device_queue_family_properties(vk_phys_device)
1194            },
1195            properties: unsafe {
1196                instance
1197                    .instance
1198                    .get_physical_device_properties(vk_phys_device)
1199            },
1200            features: unsafe {
1201                instance
1202                    .instance
1203                    .get_physical_device_features(vk_phys_device)
1204            },
1205            memory_properties: unsafe {
1206                instance
1207                    .instance
1208                    .get_physical_device_memory_properties(vk_phys_device)
1209            },
1210            // supported_format_properties: {
1211            //     // vulkan has 185 formats in ash
1212            //     let range = 0..185;
1213            //     range
1214            //         .filter_map(|format| {
1215            //             let format = vk::Format::from_raw(format);
1216            //             let format_properties = unsafe {
1217            //                 instance
1218            //                     .instance
1219            //                     .get_physical_device_format_properties(vk_phys_device, format)
1220            //             };
1221            //             if !format_properties.optimal_tiling_features.is_empty()
1222            //                 || !format_properties.buffer_features.is_empty()
1223            //                 || !format_properties.linear_tiling_features.is_empty()
1224            //             {
1225            //                 Some((format, format_properties))
1226            //             } else {
1227            //                 None
1228            //             }
1229            //         })
1230            //         .collect()
1231            // },
1232            properties2_ext_enabled: instance.properties2_ext_enabled,
1233            requested_features_chain: criteria.requested_features_chain.clone().into_inner(),
1234            ..Default::default()
1235        };
1236
1237        physical_device.name = physical_device.properties.clone().device_name.to_string();
1238
1239        let available_extensions = unsafe {
1240            instance
1241                .instance
1242                .enumerate_device_extension_properties(vk_phys_device, None)
1243        };
1244
1245        let Ok(available_extensions) = available_extensions else {
1246            return Ok(physical_device);
1247        };
1248
1249        let available_extensions_names = available_extensions
1250            .into_iter()
1251            .map(|e| e.extension_name)
1252            .collect::<BTreeSet<_>>();
1253
1254        physical_device
1255            .available_extensions
1256            .extend(available_extensions_names);
1257
1258        physical_device.properties2_ext_enabled = instance.properties2_ext_enabled;
1259
1260        let requested_features_chain = criteria.requested_features_chain.borrow();
1261        let instance_is_11 = instance.instance_version >= Version::V1_1_0;
1262        if !requested_features_chain.is_empty()
1263            && (instance_is_11 || instance.properties2_ext_enabled)
1264        {
1265            let mut supported_features = requested_features_chain.clone();
1266            let mut local_features = vk::PhysicalDeviceFeatures2::builder();
1267
1268            for node in supported_features.nodes.iter_mut() {
1269                match node {
1270                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(features) => {
1271                        local_features.push_next(features)
1272                    }
1273                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(features) => {
1274                        local_features.push_next(features)
1275                    }
1276                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(features) => {
1277                        local_features.push_next(features)
1278                    }
1279                };
1280            }
1281
1282            unsafe {
1283                instance.instance.get_physical_device_features2(
1284                    physical_device.physical_device,
1285                    &mut local_features,
1286                )
1287            };
1288
1289            physical_device.supported_features_chain = supported_features.clone();
1290        }
1291
1292        Ok(physical_device)
1293    }
1294
1295    fn select_devices(&self) -> crate::Result<BTreeSet<PhysicalDevice>> {
1296        let criteria = &self.selection_criteria;
1297        let instance = self.instance.as_ref();
1298        if criteria.require_present
1299            && !criteria.defer_surface_initialization
1300            && instance.surface.is_none()
1301        {
1302            return Err(crate::PhysicalDeviceError::NoSurfaceProvided.into());
1303        };
1304
1305        let physical_devices = unsafe { instance.instance.enumerate_physical_devices() }
1306            .map_err(|_| crate::PhysicalDeviceError::FailedToEnumeratePhysicalDevices)?;
1307        if physical_devices.is_empty() {
1308            return Err(crate::PhysicalDeviceError::NoPhysicalDevicesFound.into());
1309        };
1310
1311        let fill_out_phys_dev_with_criteria = |physical_device: &mut PhysicalDevice| {
1312            physical_device.features = criteria.required_features;
1313            let mut portability_ext_available = false;
1314            let portability_name = vk::KHR_PORTABILITY_ENUMERATION_EXTENSION.name;
1315            for ext in &physical_device.available_extensions {
1316                if criteria.enable_portability_subset && ext == &portability_name {
1317                    portability_ext_available = true;
1318                }
1319            }
1320
1321            physical_device.extensions_to_enable.clear();
1322            physical_device
1323                .extensions_to_enable
1324                .extend(criteria.required_extensions.clone());
1325
1326            if portability_ext_available {
1327                physical_device
1328                    .extensions_to_enable
1329                    .insert(portability_name);
1330            }
1331        };
1332
1333        if criteria.use_first_gpu_unconditionally {
1334            let mut device = self.populate_device_details(physical_devices[0])?;
1335            fill_out_phys_dev_with_criteria(&mut device);
1336            return Ok(BTreeSet::from([device]));
1337        };
1338
1339        let physical_devices = physical_devices
1340            .into_iter()
1341            .filter_map(|p| {
1342                let mut phys_dev = self.populate_device_details(p).ok();
1343
1344                if let Some(phys_dev) = phys_dev.as_mut() {
1345                    self.set_is_suitable(phys_dev);
1346                }
1347
1348                phys_dev.and_then(|mut phys_dev| {
1349                    if phys_dev.suitable == Suitable::No {
1350                        None
1351                    } else {
1352                        fill_out_phys_dev_with_criteria(&mut phys_dev);
1353
1354                        Some(phys_dev)
1355                    }
1356                })
1357            })
1358            .collect::<BTreeSet<_>>();
1359
1360        Ok(physical_devices)
1361    }
1362
1363    /// Select a suitable `PhysicalDevice` according to the configured criteria.
1364    ///
1365    /// Returns a `PhysicalDevice` on success or an error if no suitable device could be found.
1366    pub fn select(self) -> crate::Result<PhysicalDevice> {
1367        let devices = self.select_devices()?;
1368        #[cfg(feature = "enable_tracing")]
1369        {
1370            tracing::debug!(
1371                "Device suitability: {:#?}",
1372                devices
1373                    .iter()
1374                    .map(|d| (&d.name, &d.suitable))
1375                    .collect::<Vec<_>>()
1376            );
1377        }
1378
1379        if devices.is_empty() {
1380            Err(crate::PhysicalDeviceError::NoSuitableDevice.into())
1381        } else {
1382            Ok(unsafe { devices.into_iter().next().unwrap_unchecked() })
1383        }
1384    }
1385}
1386
1387pub struct DeviceBuilder {
1388    instance: Arc<Instance>,
1389    physical_device: PhysicalDevice,
1390    allocation_callbacks: Option<AllocationCallbacks>,
1391    // TODO: pNext chains for features
1392    // TODO: queue descriptions
1393}
1394
1395impl DeviceBuilder {
1396    pub fn new(physical_device: PhysicalDevice, instance: Arc<Instance>) -> DeviceBuilder {
1397        Self {
1398            physical_device,
1399            allocation_callbacks: None,
1400            instance,
1401        }
1402    }
1403
1404    pub fn allocation_callbacks(mut self, allocation_callbacks: AllocationCallbacks) -> Self {
1405        self.allocation_callbacks.replace(allocation_callbacks);
1406        self
1407    }
1408
1409    /// Create a logical `Device` from the configured `PhysicalDevice`.
1410    ///
1411    /// What this does:
1412    /// - Builds queue create infos for each discovered queue family (default priority 1.0).
1413    /// - Enables any device extensions that were marked on the `PhysicalDevice` (and the
1414    ///   `VK_KHR_swapchain` extension when a surface is present or surface init is deferred).
1415    /// - Pushes a `vk::PhysicalDeviceFeatures2` and any requested feature-chain nodes onto the
1416    ///   device create pNext chain when the instance supports properties2 or is Vulkan 1.1+.
1417    /// - Calls `vkCreateDevice` and returns a `Device` wrapper on success.
1418    ///
1419    /// Returns:
1420    /// - `Ok(Device)` containing the created `vulkanalia::Device`, retained `Instance` and
1421    ///   selected `PhysicalDevice` information.
1422    /// - An error if device creation fails.
1423    ///
1424    /// Notes:
1425    /// - Queue configuration is simplified: every queue family discovered by the physical
1426    ///   device is created with a single queue at priority 1.0. Customize if you need
1427    ///   different priorities or explicit queue counts.
1428    /// - Any allocation callbacks previously set via `DeviceBuilder::allocation_callbacks`
1429    ///   are forwarded to `vkCreateDevice` and stored in the returned `Device`.
1430    pub fn build(mut self) -> crate::Result<Device> {
1431        // TODO: custom queue setup
1432        // (index, priorities)
1433        let queue_descriptions = self
1434            .physical_device
1435            .queue_families
1436            .iter()
1437            .enumerate()
1438            .map(|(index, _)| (index, [1.]))
1439            .collect::<Vec<_>>();
1440
1441        let queue_create_infos = queue_descriptions
1442            .iter()
1443            .map(|(index, priorities)| {
1444                vk::DeviceQueueCreateInfo::builder()
1445                    .queue_family_index(*index as u32)
1446                    .queue_priorities(priorities)
1447            })
1448            .collect::<Vec<_>>();
1449
1450        let mut extensions_to_enable = self
1451            .physical_device
1452            .extensions_to_enable
1453            .iter()
1454            .map(|ext| ext.as_ptr())
1455            .collect::<Vec<_>>();
1456
1457        if self.physical_device.surface.is_some()
1458            || self.physical_device.defer_surface_initialization
1459        {
1460            extensions_to_enable.push(vk::KHR_SWAPCHAIN_EXTENSION.name.as_ptr());
1461        }
1462
1463        let mut device_create_info = vk::DeviceCreateInfo::builder()
1464            .queue_create_infos(&queue_create_infos)
1465            .enabled_extension_names(&extensions_to_enable);
1466
1467        let requested_features_chain = &mut self.physical_device.requested_features_chain;
1468
1469        let mut features2 =
1470            vk::PhysicalDeviceFeatures2::builder().features(self.physical_device.features);
1471
1472        if self.instance.instance_version >= Version::V1_1_0
1473            || self.physical_device.properties2_ext_enabled
1474        {
1475            device_create_info = device_create_info.push_next(&mut features2);
1476
1477            for node in requested_features_chain.nodes.iter_mut() {
1478                match node {
1479                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan11(f) => {
1480                        device_create_info = device_create_info.push_next(f)
1481                    }
1482                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan12(f) => {
1483                        device_create_info = device_create_info.push_next(f)
1484                    }
1485                    VulkanPhysicalDeviceFeature2::PhysicalDeviceVulkan13(f) => {
1486                        device_create_info = device_create_info.push_next(f)
1487                    }
1488                }
1489            }
1490        }
1491
1492        let device = unsafe {
1493            self.instance.instance.create_device(
1494                self.physical_device.physical_device,
1495                &device_create_info,
1496                self.allocation_callbacks.as_ref(),
1497            )
1498        }?;
1499
1500        let instance = self.instance;
1501        let physical_device = self.physical_device;
1502
1503        let surface = physical_device.surface;
1504        let allocation_callbacks = self.allocation_callbacks;
1505
1506        Ok(Device {
1507            instance,
1508            device,
1509            surface,
1510            physical_device,
1511            allocation_callbacks,
1512        })
1513    }
1514}
1515
1516#[derive(Debug)]
1517pub struct Device {
1518    instance: Arc<Instance>,
1519    device: vulkanalia::Device,
1520    physical_device: PhysicalDevice,
1521    surface: Option<vk::SurfaceKHR>,
1522    allocation_callbacks: Option<AllocationCallbacks>,
1523}
1524
1525#[derive(Debug, Clone, PartialOrd, PartialEq, Eq, Ord)]
1526pub enum QueueType {
1527    Present,
1528    Graphics,
1529    Compute,
1530    Transfer,
1531}
1532
1533impl Device {
1534    pub fn device(&self) -> &vulkanalia::Device {
1535        &self.device
1536    }
1537
1538    pub fn physical_device(&self) -> &PhysicalDevice {
1539        &self.physical_device
1540    }
1541
1542    pub fn get_queue(&self, queue: QueueType) -> crate::Result<(usize, vk::Queue)> {
1543        let index = match queue {
1544            QueueType::Present => get_present_queue_index(
1545                &self.instance.instance,
1546                self.physical_device.physical_device,
1547                self.surface,
1548                &self.physical_device.queue_families,
1549            )
1550            .ok_or(crate::QueueError::PresentUnavailable),
1551            QueueType::Graphics => get_first_queue_index(
1552                &self.physical_device.queue_families,
1553                vk::QueueFlags::GRAPHICS,
1554            )
1555            .ok_or(crate::QueueError::GraphicsUnavailable),
1556            QueueType::Compute => get_separate_queue_index(
1557                &self.physical_device.queue_families,
1558                vk::QueueFlags::COMPUTE,
1559                vk::QueueFlags::TRANSFER,
1560            )
1561            .ok_or(crate::QueueError::ComputeUnavailable),
1562            QueueType::Transfer => get_separate_queue_index(
1563                &self.physical_device.queue_families,
1564                vk::QueueFlags::TRANSFER,
1565                vk::QueueFlags::COMPUTE,
1566            )
1567            .ok_or(crate::QueueError::TransferUnavailable),
1568        }?;
1569
1570        Ok((index, unsafe {
1571            self.device.get_device_queue(index as _, 0)
1572        }))
1573    }
1574
1575    pub fn get_dedicated_queue(&self, queue: QueueType) -> crate::Result<vk::Queue> {
1576        let index = match queue {
1577            QueueType::Compute => get_dedicated_queue_index(
1578                &self.physical_device.queue_families,
1579                vk::QueueFlags::COMPUTE,
1580                vk::QueueFlags::TRANSFER,
1581            )
1582            .ok_or(crate::QueueError::ComputeUnavailable),
1583            QueueType::Transfer => get_dedicated_queue_index(
1584                &self.physical_device.queue_families,
1585                vk::QueueFlags::TRANSFER,
1586                vk::QueueFlags::COMPUTE,
1587            )
1588            .ok_or(crate::QueueError::TransferUnavailable),
1589            _ => return Err(crate::QueueError::InvalidQueueFamilyIndex.into()),
1590        }?;
1591
1592        let info = vk::DeviceQueueInfo2::builder()
1593            .queue_family_index(index as _)
1594            .queue_index(0);
1595
1596        Ok(unsafe { self.device.get_device_queue2(&info) })
1597    }
1598
1599    pub fn destroy(&self) {
1600        unsafe {
1601            self.device
1602                .destroy_device(self.allocation_callbacks.as_ref());
1603        }
1604    }
1605}
1606
1607impl AsRef<vulkanalia::Device> for Device {
1608    fn as_ref(&self) -> &vulkanalia::Device {
1609        &self.device
1610    }
1611}
1612
1613impl Deref for Device {
1614    type Target = vulkanalia::Device;
1615
1616    fn deref(&self) -> &Self::Target {
1617        &self.device
1618    }
1619}