1use crate::InstanceMetadata;
3use erupt::{vk, DeviceLoader, DeviceLoaderBuilder, ExtendableFrom, InstanceLoader, LoaderError, SmallVec, ObjectHandle};
4use std::{
5 borrow::Cow,
6 collections::HashSet,
7 ffi::{CStr, CString},
8 hash::{Hash, Hasher},
9 os::raw::{c_char, c_float},
10};
11use thiserror::Error;
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)]
15pub struct QueueFamilyCriteria {
16 pub must_support: vk::QueueFlags,
18 pub should_support: vk::QueueFlags,
22 pub must_not_support: vk::QueueFlags,
25 pub should_not_support: vk::QueueFlags,
29 pub presentation_support: Option<bool>,
34}
35
36impl QueueFamilyCriteria {
37 #[inline]
39 pub fn none() -> QueueFamilyCriteria {
40 QueueFamilyCriteria::default()
41 }
42
43 #[inline]
46 pub fn graphics_present() -> QueueFamilyCriteria {
47 QueueFamilyCriteria::none()
48 .must_support(vk::QueueFlags::GRAPHICS)
49 .must_support_presentation()
50 }
51
52 #[inline]
55 pub fn preferably_separate_transfer() -> QueueFamilyCriteria {
56 QueueFamilyCriteria::none()
57 .must_support(vk::QueueFlags::TRANSFER)
58 .should_not_support(!vk::QueueFlags::TRANSFER)
59 }
60
61 #[inline]
64 pub fn must_support(mut self, must_support: vk::QueueFlags) -> QueueFamilyCriteria {
65 self.must_support |= must_support;
66 self
67 }
68
69 #[inline]
72 pub fn should_support(mut self, should_support: vk::QueueFlags) -> QueueFamilyCriteria {
73 self.should_support |= should_support;
74 self
75 }
76
77 #[inline]
80 pub fn must_not_support(mut self, must_not_support: vk::QueueFlags) -> QueueFamilyCriteria {
81 self.must_not_support |= must_not_support;
82 self
83 }
84
85 #[inline]
88 pub fn should_not_support(mut self, should_not_support: vk::QueueFlags) -> QueueFamilyCriteria {
89 self.should_not_support |= should_not_support;
90 self
91 }
92
93 #[inline]
95 pub fn must_support_presentation(mut self) -> QueueFamilyCriteria {
96 self.presentation_support = Some(true);
97 self
98 }
99
100 #[inline]
102 pub fn must_not_support_presentation(mut self) -> QueueFamilyCriteria {
103 self.presentation_support = Some(false);
104 self
105 }
106
107 pub fn choose_queue_family<'a>(
111 &self,
112 instance: &InstanceLoader,
113 physical_device: vk::PhysicalDevice,
114 queue_family_properties: &'a [vk::QueueFamilyProperties],
115 surface: Option<vk::SurfaceKHR>,
116 ) -> Result<Option<(u32, &'a vk::QueueFamilyProperties)>, vk::Result> {
117 let mut candidates = SmallVec::new();
118 for (i, queue_family_properties) in queue_family_properties.iter().enumerate() {
119 let i = i as u32;
120
121 let positive_required = queue_family_properties
122 .queue_flags
123 .contains(self.must_support);
124
125 let negative_required = !queue_family_properties
126 .queue_flags
127 .intersects(self.must_not_support);
128
129 let presentation = || {
130 Ok(match (self.presentation_support, surface) {
131 (None, _) => true,
132 (Some(_), None) => false,
133 (Some(expected), Some(surface)) => unsafe {
134 let support = instance
135 .get_physical_device_surface_support_khr(physical_device, i, surface)
136 .result()?;
137
138 support == expected
139 },
140 })
141 };
142
143 if positive_required && negative_required && presentation()? {
144 candidates.push((i, queue_family_properties));
145 }
146 }
147
148 let best_candidate = candidates
149 .into_iter()
150 .max_by_key(|(_, queue_family_properties)| {
151 let positive_recommended = self
152 .should_support
153 .intersection(queue_family_properties.queue_flags)
154 .bits()
155 .count_ones();
156
157 let negative_recommended = self
158 .should_not_support
159 .difference(queue_family_properties.queue_flags)
160 .bits()
161 .count_ones();
162
163 positive_recommended + negative_recommended
164 });
165
166 Ok(best_candidate)
167 }
168}
169
170#[derive(Debug, Error)]
172pub enum DeviceCreationError {
173 #[error("vulkan error")]
175 VulkanError(#[from] vk::Result),
176 #[error("no physical device at specified index")]
178 UnconditionalMissing,
179 #[error("no physical device met the requirements")]
181 RequirementsNotMet,
182 #[error("loader creation error")]
184 LoaderCreation(#[from] LoaderError),
185}
186
187#[derive(Debug, Clone)]
191pub struct QueueSetup {
192 pub flags: vk::DeviceQueueCreateFlags,
194 pub queue_family_index: u32,
196 pub queue_priorities: Vec<c_float>,
198}
199
200impl QueueSetup {
201 #[inline]
204 pub fn simple(queue_family_index: u32, queue_count: usize) -> QueueSetup {
205 QueueSetup {
206 flags: vk::DeviceQueueCreateFlags::empty(),
207 queue_family_index,
208 queue_priorities: (0..queue_count).map(|_| 1.0).collect(),
209 }
210 }
211
212 #[inline]
213 fn as_vulkan(&self) -> vk::DeviceQueueCreateInfoBuilder {
214 vk::DeviceQueueCreateInfoBuilder::new()
215 .flags(self.flags)
216 .queue_family_index(self.queue_family_index)
217 .queue_priorities(&self.queue_priorities)
218 }
219}
220
221impl PartialEq for QueueSetup {
222 fn eq(&self, rhs: &Self) -> bool {
223 self.queue_family_index == rhs.queue_family_index
224 }
225}
226
227impl Eq for QueueSetup {}
228
229impl Hash for QueueSetup {
230 fn hash<H: Hasher>(&self, state: &mut H) {
231 self.queue_family_index.hash(state);
232 }
233}
234
235#[derive(Debug, Clone)]
237pub struct DeviceMetadata {
238 device_handle: vk::Device,
239 physical_device: vk::PhysicalDevice,
240 properties: vk::PhysicalDeviceProperties,
241 queue_setups: SmallVec<QueueSetup>,
242 memory_properties: vk::PhysicalDeviceMemoryProperties,
243 queue_family_properties: SmallVec<vk::QueueFamilyProperties>,
244 surface: Option<vk::SurfaceKHR>,
245 enabled_extensions: SmallVec<CString>,
246}
247
248impl DeviceMetadata {
249 #[inline]
251 pub fn device_handle(&self) -> vk::Device {
252 self.device_handle
253 }
254
255 #[inline]
257 pub fn physical_device(&self) -> vk::PhysicalDevice {
258 self.physical_device
259 }
260
261 #[inline]
263 pub fn surface(&self) -> Option<vk::SurfaceKHR> {
264 self.surface
265 }
266
267 #[inline]
269 pub fn properties(&self) -> &vk::PhysicalDeviceProperties {
270 &self.properties
271 }
272
273 #[inline]
275 pub fn device_name(&self) -> Cow<str> {
276 unsafe { CStr::from_ptr(self.properties.device_name.as_ptr()).to_string_lossy() }
277 }
278
279 #[inline]
281 pub fn device_type(&self) -> vk::PhysicalDeviceType {
282 self.properties.device_type
283 }
284
285 #[inline]
289 pub fn device_queue(
290 &self,
291 instance: &InstanceLoader,
292 device: &DeviceLoader,
293 criteria: QueueFamilyCriteria,
294 queue_index: u32,
295 ) -> Result<Option<(vk::Queue, u32)>, vk::Result> {
296 let queue_family = criteria.choose_queue_family(
297 instance,
298 self.physical_device,
299 &self.queue_family_properties,
300 self.surface,
301 )?;
302
303 Ok(queue_family.and_then(|(idx, _properties)| unsafe {
304 let handle = device.get_device_queue(idx, queue_index);
305 (!handle.is_null()).then(|| (handle, idx))
306 }))
307 }
308
309 #[inline]
311 pub fn queue_setups(&self) -> &[QueueSetup] {
312 &self.queue_setups
313 }
314
315 #[inline]
317 pub fn memory_properties(&self) -> &vk::PhysicalDeviceMemoryProperties {
318 &self.memory_properties
319 }
320
321 #[inline]
323 pub fn queue_family_properties(&self) -> &[vk::QueueFamilyProperties] {
324 &self.queue_family_properties
325 }
326
327 #[inline]
329 pub fn enabled_extensions(&self) -> &[CString] {
330 &self.enabled_extensions
331 }
332
333 #[inline]
335 pub unsafe fn is_extension_enabled(&self, extension: *const c_char) -> bool {
336 let qry = CStr::from_ptr(extension);
337 self.enabled_extensions.iter().any(|i| i.as_c_str() == qry)
338 }
339}
340
341pub type CustomQueueSetupFn = dyn FnMut(
344 vk::PhysicalDevice,
345 &[QueueFamilyCriteria],
346 &[vk::QueueFamilyProperties],
347) -> Result<Option<HashSet<QueueSetup>>, vk::Result>;
348
349pub enum DeviceSuitability {
351 Perfect,
354 NotPreferred,
358 Unsuitable,
361}
362
363impl From<bool> for DeviceSuitability {
364 fn from(suitable_perfect: bool) -> Self {
365 if suitable_perfect {
366 DeviceSuitability::Perfect
367 } else {
368 DeviceSuitability::Unsuitable
369 }
370 }
371}
372
373pub type AdditionalSuitabilityFn =
376 dyn FnMut(&InstanceLoader, vk::PhysicalDevice) -> DeviceSuitability;
377
378pub struct DeviceBuilder<'a> {
380 loader_builder: DeviceLoaderBuilder<'a>,
381 queue_setup_fn: Option<Box<CustomQueueSetupFn>>,
382 additional_suitability_fn: Option<Box<AdditionalSuitabilityFn>>,
383 surface: Option<vk::SurfaceKHR>,
384 prioritised_device_types: SmallVec<vk::PhysicalDeviceType>,
385 queue_family_criteria: SmallVec<QueueFamilyCriteria>,
386 preferred_device_memory_size: Option<vk::DeviceSize>,
387 required_device_memory_size: Option<vk::DeviceSize>,
388 extensions: SmallVec<(*const c_char, bool)>,
389 preferred_version: Option<u32>,
390 required_version: u32,
391 required_features: Option<&'a vk::PhysicalDeviceFeatures2>,
392 unconditional_nth: Option<usize>,
393 allocator: Option<vk::AllocationCallbacks>,
394}
395
396impl<'a> DeviceBuilder<'a> {
397 #[inline]
399 pub fn new() -> Self {
400 DeviceBuilder::with_loader_builder(DeviceLoaderBuilder::new())
401 }
402
403 pub fn with_loader_builder(loader_builder: DeviceLoaderBuilder<'a>) -> Self {
405 DeviceBuilder {
406 loader_builder,
407 queue_setup_fn: None,
408 additional_suitability_fn: None,
409 surface: None,
410 prioritised_device_types: SmallVec::new(),
411 queue_family_criteria: SmallVec::new(),
412 preferred_device_memory_size: None,
413 required_device_memory_size: None,
414 extensions: SmallVec::new(),
415 preferred_version: None,
416 required_version: vk::API_VERSION_1_0,
417 required_features: None,
418 unconditional_nth: None,
419 allocator: None,
420 }
421 }
422
423 #[inline]
448 pub fn custom_queue_setup(mut self, custom_queue_setup: Box<CustomQueueSetupFn>) -> Self {
449 self.queue_setup_fn = Some(custom_queue_setup);
450 self
451 }
452
453 #[inline]
456 pub fn additional_suitability(
457 mut self,
458 additional_suitability: Box<AdditionalSuitabilityFn>,
459 ) -> Self {
460 self.additional_suitability_fn = Some(additional_suitability);
461 self
462 }
463
464 #[inline]
466 pub fn for_surface(mut self, surface: vk::SurfaceKHR) -> Self {
467 self.surface = Some(surface);
468 self
469 }
470
471 #[inline]
474 pub fn prioritise_device_types(mut self, types: &[vk::PhysicalDeviceType]) -> Self {
475 self.prioritised_device_types = types.into();
476 self
477 }
478
479 #[inline]
481 pub fn queue_family(mut self, criteria: QueueFamilyCriteria) -> Self {
482 self.queue_family_criteria.push(criteria);
483 self
484 }
485
486 #[inline]
489 pub fn prefer_device_memory_size(mut self, size: vk::DeviceSize) -> Self {
490 self.preferred_device_memory_size = Some(size);
491 self
492 }
493
494 #[inline]
497 pub fn require_device_memory_size(mut self, size: vk::DeviceSize) -> Self {
498 self.required_device_memory_size = Some(size);
499 self
500 }
501
502 #[inline]
505 pub fn prefer_extension(mut self, extension: *const c_char) -> Self {
506 self.extensions.push((extension, false));
507 self
508 }
509
510 #[inline]
513 pub fn require_extension(mut self, extension: *const c_char) -> Self {
514 self.extensions.push((extension, true));
515 self
516 }
517
518 #[inline]
520 pub fn prefer_version(mut self, major: u32, minor: u32) -> Self {
521 self.preferred_version = Some(vk::make_api_version(0, major, minor, 0));
522 self
523 }
524
525 #[inline]
527 pub fn prefer_version_raw(mut self, version: u32) -> Self {
528 self.preferred_version = Some(version);
529 self
530 }
531
532 #[inline]
534 pub fn require_version(mut self, major: u32, minor: u32) -> Self {
535 self.required_version = vk::make_api_version(0, major, minor, 0);
536 self
537 }
538
539 #[inline]
541 pub fn require_version_raw(mut self, version: u32) -> Self {
542 self.required_version = version;
543 self
544 }
545
546 #[inline]
550 pub fn require_features(mut self, features: &'a vk::PhysicalDeviceFeatures2) -> Self {
551 self.required_features = Some(features);
552 self
553 }
554
555 #[inline]
558 pub fn select_nth_unconditionally(mut self, n: usize) -> Self {
559 self.unconditional_nth = Some(n);
560 self
561 }
562
563 #[inline]
565 pub fn allocation_callbacks(mut self, allocator: vk::AllocationCallbacks) -> Self {
566 self.allocator = Some(allocator);
567 self
568 }
569
570 pub unsafe fn build(
574 mut self,
575 instance: &'a InstanceLoader,
576 instance_metadata: &InstanceMetadata,
577 ) -> Result<(DeviceLoader, DeviceMetadata), DeviceCreationError> {
578 assert_eq!(instance.handle, instance_metadata.instance_handle());
579
580 let mut queue_setup_fn = self.queue_setup_fn.unwrap_or_else(|| {
581 Box::new(
584 |physical_device, queue_family_criteria, queue_family_properties| {
585 let mut queue_setup = HashSet::with_capacity(queue_family_criteria.len());
586 for queue_family_criteria in queue_family_criteria {
587 match queue_family_criteria.choose_queue_family(
588 instance,
589 physical_device,
590 queue_family_properties,
591 self.surface,
592 )? {
593 Some((idx, _properties)) => {
594 queue_setup.insert(QueueSetup::simple(idx, 1));
595 }
596 None => return Ok(None),
597 }
598 }
599
600 Ok(Some(queue_setup))
601 },
602 )
603 });
604
605 let physical_devices = instance.enumerate_physical_devices(None).result()?;
606 let mut devices_properties = physical_devices.into_iter().map(|physical_device| {
607 (physical_device, unsafe {
608 instance.get_physical_device_properties(physical_device)
609 })
610 });
611
612 let devices = if let Some(n) = self.unconditional_nth {
613 vec![devices_properties
614 .nth(n)
615 .ok_or(DeviceCreationError::UnconditionalMissing)?]
616 } else {
617 let mut device_type_preference = self.prioritised_device_types;
618 device_type_preference.extend([
619 vk::PhysicalDeviceType::DISCRETE_GPU,
620 vk::PhysicalDeviceType::INTEGRATED_GPU,
621 ]);
622
623 let mut devices_properties: Vec<_> = devices_properties.collect();
624 devices_properties.sort_by_key(|(_physical_device, properties)| {
625 device_type_preference
626 .iter()
627 .position(|&preference| properties.device_type == preference)
628 .unwrap_or(usize::MAX)
629 });
630
631 devices_properties
632 };
633
634 struct Candidate {
635 physical_device: vk::PhysicalDevice,
636 properties: vk::PhysicalDeviceProperties,
637 queue_setups: SmallVec<QueueSetup>,
638 memory_properties: vk::PhysicalDeviceMemoryProperties,
639 queue_family_properties: SmallVec<vk::QueueFamilyProperties>,
640 enabled_extensions: SmallVec<*const c_char>,
641 }
642
643 let mut perfect_candidates = SmallVec::new();
644 let mut inperfect_candidates = SmallVec::new();
645 for (physical_device, properties) in devices {
646 let mut perfect_candidate = true;
647
648 if self.required_version > properties.api_version {
649 continue;
650 }
651
652 if let Some(preferred_version) = self.preferred_version {
653 if preferred_version > properties.api_version {
654 perfect_candidate = false;
655 }
656 }
657
658 let memory_properties = instance.get_physical_device_memory_properties(physical_device);
659 let queue_family_properties =
660 instance.get_physical_device_queue_family_properties(physical_device, None);
661
662 if self.preferred_device_memory_size.is_some()
663 || self.required_device_memory_size.is_some()
664 {
665 let highest_device_local_memory = memory_properties
666 .memory_heaps
667 .into_iter()
668 .take(memory_properties.memory_heap_count as usize)
669 .filter(|memory_heap| {
670 memory_heap
671 .flags
672 .contains(vk::MemoryHeapFlags::DEVICE_LOCAL)
673 })
674 .map(|memory_heap| memory_heap.size)
675 .max()
676 .expect(
677 "spec violation: At least one heap must include \
678 VK_MEMORY_HEAP_DEVICE_LOCAL_BIT in VkMemoryHeap::flags.",
679 );
680
681 if let Some(preferred_device_memory_size) = self.preferred_device_memory_size {
682 if preferred_device_memory_size > highest_device_local_memory {
683 perfect_candidate = false;
684 }
685 }
686
687 if let Some(required_device_memory_size) = self.required_device_memory_size {
688 if required_device_memory_size > highest_device_local_memory {
689 continue;
690 }
691 }
692 }
693
694 let queue_setup = if self.queue_family_criteria.is_empty() {
695 SmallVec::new()
696 } else {
697 match queue_setup_fn(
698 physical_device,
699 &self.queue_family_criteria,
700 &queue_family_properties,
701 )? {
702 Some(queue_setup) => queue_setup.into_iter().collect(),
703 None => continue,
704 }
705 };
706
707 let enabled_extensions = if self.extensions.is_empty() {
708 SmallVec::new()
709 } else {
710 let mut extension_properties = instance
711 .enumerate_device_extension_properties(physical_device, None, None)
712 .result()?;
713 for layer in instance_metadata.enabled_layers() {
714 let cstr = layer.as_c_str();
715 let extensions = instance
716 .enumerate_device_extension_properties(physical_device, Some(cstr), None)
717 .result()?;
718 extension_properties.extend(extensions);
719 }
720
721 let mut enabled_extensions = SmallVec::new();
722 for &(extension_name, required) in self.extensions.iter() {
723 let cstr = CStr::from_ptr(extension_name);
724 let present = extension_properties.iter().any(|supported_extension| {
725 CStr::from_ptr(supported_extension.extension_name.as_ptr()) == cstr
726 });
727
728 if present {
729 enabled_extensions.push(extension_name);
730 } else if required {
731 continue;
732 } else {
733 perfect_candidate = false;
734 }
735 }
736
737 enabled_extensions
738 };
739
740 let candidate = Candidate {
741 physical_device,
742 properties,
743 queue_setups: queue_setup,
744 memory_properties,
745 queue_family_properties,
746 enabled_extensions,
747 };
748
749 if let Some(additional_suitability) = self.additional_suitability_fn.as_mut() {
750 match additional_suitability(instance, physical_device) {
751 DeviceSuitability::Perfect => (),
752 DeviceSuitability::NotPreferred => perfect_candidate = false,
753 DeviceSuitability::Unsuitable => continue,
754 }
755 }
756
757 if perfect_candidate {
758 perfect_candidates.push(candidate);
759 } else {
760 inperfect_candidates.push(candidate);
761 }
762 }
763
764 let features2_supported = instance_metadata.api_version_raw() >= vk::API_VERSION_1_1
765 || instance_metadata
766 .is_extension_enabled(vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
767 for candidate in perfect_candidates
768 .into_iter()
769 .chain(inperfect_candidates.into_iter())
770 {
771 let queue_create_infos: SmallVec<_> = candidate
772 .queue_setups
773 .iter()
774 .map(QueueSetup::as_vulkan)
775 .collect();
776 let mut device_info = vk::DeviceCreateInfoBuilder::new()
777 .queue_create_infos(&queue_create_infos)
778 .enabled_extension_names(&candidate.enabled_extensions);
779
780 let mut required_features;
781 if let Some(&val) = self.required_features {
782 required_features = val;
783
784 if features2_supported {
785 device_info = device_info.extend_from(&mut required_features);
786 } else {
787 device_info = device_info.enabled_features(&required_features.features);
788 }
789 }
790
791 let device_handle = instance
792 .create_device(
793 candidate.physical_device,
794 &device_info,
795 self.allocator.as_ref(),
796 )
797 .result();
798 match device_handle {
799 Ok(device_handle) => {
800 let device = self.loader_builder.build_with_existing_device(
801 instance,
802 device_handle,
803 &device_info,
804 )?;
805
806 drop(queue_create_infos);
807 let device_metadata = DeviceMetadata {
808 device_handle,
809 physical_device: candidate.physical_device,
810 properties: candidate.properties,
811 queue_setups: candidate.queue_setups,
812 memory_properties: candidate.memory_properties,
813 queue_family_properties: candidate.queue_family_properties,
814 surface: self.surface,
815 enabled_extensions: candidate
816 .enabled_extensions
817 .into_iter()
818 .map(|ptr| unsafe { CStr::from_ptr(ptr).to_owned() })
819 .collect(),
820 };
821
822 return Ok((device, device_metadata));
823 }
824 Err(vk::Result::ERROR_FEATURE_NOT_PRESENT) => continue,
825 Err(err) => return Err(err.into()),
826 }
827 }
828
829 Err(DeviceCreationError::RequirementsNotMet)
830 }
831}
832
833impl<'a> Default for DeviceBuilder<'a> {
834 fn default() -> Self {
835 Self::new()
836 }
837}