1use {
4 super::{DriverError, Instance, physical_device::PhysicalDevice},
5 ash::{ext, khr, vk},
6 ash_window::enumerate_required_extensions,
7 derive_builder::{Builder, UninitializedFieldError},
8 gpu_allocator::{
9 AllocatorDebugSettings,
10 vulkan::{Allocator, AllocatorCreateDesc},
11 },
12 log::{error, trace, warn},
13 raw_window_handle::HasDisplayHandle,
14 std::{
15 cmp::Ordering,
16 ffi::CStr,
17 fmt::{Debug, Formatter},
18 iter::{empty, repeat},
19 mem::{ManuallyDrop, forget},
20 ops::Deref,
21 thread::panicking,
22 time::Instant,
23 },
24};
25
26#[cfg(feature = "parking_lot")]
27use parking_lot::Mutex;
28
29#[cfg(not(feature = "parking_lot"))]
30use std::sync::Mutex;
31
32pub type SelectPhysicalDeviceFn = dyn FnOnce(&[PhysicalDevice]) -> usize;
34
35pub struct Device {
37 accel_struct_ext: Option<khr::acceleration_structure::Device>,
38
39 pub(super) allocator: ManuallyDrop<Mutex<Allocator>>,
40
41 device: ash::Device,
42
43 instance: Instance,
45
46 pipeline_cache: vk::PipelineCache,
47
48 pub physical_device: PhysicalDevice,
50
51 pub(crate) queues: Vec<Vec<vk::Queue>>,
53
54 pub(crate) ray_trace_ext: Option<khr::ray_tracing_pipeline::Device>,
55
56 pub(super) surface_ext: Option<khr::surface::Instance>,
57 pub(super) swapchain_ext: Option<khr::swapchain::Device>,
58}
59
60impl Device {
61 #[profiling::function]
69 pub unsafe fn create_ash_device<F>(
70 instance: &Instance,
71 physical_device: &PhysicalDevice,
72 display_window: bool,
73 create_fn: F,
74 ) -> ash::prelude::VkResult<ash::Device>
75 where
76 F: FnOnce(vk::DeviceCreateInfo) -> ash::prelude::VkResult<ash::Device>,
77 {
78 let mut enabled_ext_names = Vec::with_capacity(6);
79
80 if display_window {
81 enabled_ext_names.push(khr::swapchain::NAME.as_ptr());
82 }
83
84 if physical_device.accel_struct_properties.is_some() {
85 enabled_ext_names.push(khr::acceleration_structure::NAME.as_ptr());
86 enabled_ext_names.push(khr::deferred_host_operations::NAME.as_ptr());
87 }
88
89 if physical_device.ray_query_features.ray_query {
90 enabled_ext_names.push(khr::ray_query::NAME.as_ptr());
91 }
92
93 if physical_device.ray_trace_features.ray_tracing_pipeline {
94 enabled_ext_names.push(khr::ray_tracing_pipeline::NAME.as_ptr());
95 }
96
97 if physical_device.index_type_uint8_features.index_type_uint8 {
98 enabled_ext_names.push(ext::index_type_uint8::NAME.as_ptr());
99 }
100
101 let priorities = repeat(1.0)
102 .take(
103 physical_device
104 .queue_families
105 .iter()
106 .map(|family| family.queue_count)
107 .max()
108 .unwrap_or_default() as _,
109 )
110 .collect::<Box<_>>();
111
112 let queue_infos = physical_device
113 .queue_families
114 .iter()
115 .enumerate()
116 .map(|(idx, family)| {
117 let mut queue_info = vk::DeviceQueueCreateInfo::default()
118 .queue_family_index(idx as _)
119 .queue_priorities(&priorities[0..family.queue_count as usize]);
120 queue_info.queue_count = family.queue_count;
121
122 queue_info
123 })
124 .collect::<Box<_>>();
125
126 let ash::InstanceFnV1_1 {
127 get_physical_device_features2,
128 ..
129 } = instance.fp_v1_1();
130 let mut features_v1_1 = vk::PhysicalDeviceVulkan11Features::default();
131 let mut features_v1_2 = vk::PhysicalDeviceVulkan12Features::default();
132 let mut acceleration_structure_features =
133 vk::PhysicalDeviceAccelerationStructureFeaturesKHR::default();
134 let mut index_type_uint8_features = vk::PhysicalDeviceIndexTypeUint8FeaturesEXT::default();
135 let mut ray_query_features = vk::PhysicalDeviceRayQueryFeaturesKHR::default();
136 let mut ray_trace_features = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR::default();
137 let mut features = vk::PhysicalDeviceFeatures2::default()
138 .push_next(&mut features_v1_1)
139 .push_next(&mut features_v1_2);
140
141 if physical_device.accel_struct_properties.is_some() {
142 features = features.push_next(&mut acceleration_structure_features);
143 }
144
145 if physical_device.ray_query_features.ray_query {
146 features = features.push_next(&mut ray_query_features);
147 }
148
149 if physical_device.ray_trace_features.ray_tracing_pipeline {
150 features = features.push_next(&mut ray_trace_features);
151 }
152
153 if physical_device.index_type_uint8_features.index_type_uint8 {
154 features = features.push_next(&mut index_type_uint8_features);
155 }
156
157 unsafe { get_physical_device_features2(**physical_device, &mut features) };
158
159 let device_create_info = vk::DeviceCreateInfo::default()
160 .queue_create_infos(&queue_infos)
161 .enabled_extension_names(&enabled_ext_names)
162 .push_next(&mut features);
163
164 create_fn(device_create_info)
165 }
166
167 #[profiling::function]
168 fn create(
169 instance: Instance,
170 select_physical_device: Box<SelectPhysicalDeviceFn>,
171 display_window: bool,
172 ) -> Result<Self, DriverError> {
173 let mut physical_devices = Instance::physical_devices(&instance)?;
174
175 if physical_devices.is_empty() {
176 error!("no supported devices found");
177
178 return Err(DriverError::Unsupported);
179 }
180
181 let mut phyical_device_idx = select_physical_device(&physical_devices);
182
183 if phyical_device_idx >= physical_devices.len() {
184 warn!("invalid device selected");
185
186 phyical_device_idx = 0;
187 }
188
189 let physical_device = physical_devices.remove(phyical_device_idx);
190
191 let device = unsafe {
192 Self::create_ash_device(
193 &instance,
194 &physical_device,
195 display_window,
196 |device_create_info| {
197 instance.create_device(*physical_device, &device_create_info, None)
198 },
199 )
200 }
201 .map_err(|err| {
202 error!("unable to create device: {err}");
203
204 DriverError::Unsupported
205 })?;
206
207 Self::load(instance, physical_device, device, display_window)
208 }
209
210 #[profiling::function]
212 pub fn create_headless(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
213 let DeviceInfo {
214 debug,
215 select_physical_device,
216 } = info.into();
217 let instance = Instance::create(debug, empty())?;
218
219 Self::create(instance, select_physical_device, false)
220 }
221
222 #[profiling::function]
224 pub fn create_display(
225 info: impl Into<DeviceInfo>,
226 display_handle: &impl HasDisplayHandle,
227 ) -> Result<Self, DriverError> {
228 let DeviceInfo {
229 debug,
230 select_physical_device,
231 } = info.into();
232 let display_handle = display_handle.display_handle().map_err(|err| {
233 warn!("{err}");
234
235 DriverError::Unsupported
236 })?;
237 let required_extensions = enumerate_required_extensions(display_handle.as_raw())
238 .map_err(|err| {
239 warn!("{err}");
240
241 DriverError::Unsupported
242 })?
243 .iter()
244 .map(|ext| unsafe { CStr::from_ptr(*ext as *const _) });
245 let instance = Instance::create(debug, required_extensions)?;
246
247 Self::create(instance, select_physical_device, true)
248 }
249
250 pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result<vk::Fence, DriverError> {
251 let mut flags = vk::FenceCreateFlags::empty();
252
253 if signaled {
254 flags |= vk::FenceCreateFlags::SIGNALED;
255 }
256
257 let create_info = vk::FenceCreateInfo::default().flags(flags);
258 let allocation_callbacks = None;
259
260 unsafe { this.create_fence(&create_info, allocation_callbacks) }.map_err(|err| {
261 warn!("{err}");
262
263 DriverError::OutOfMemory
264 })
265 }
266
267 pub(crate) fn create_semaphore(this: &Self) -> Result<vk::Semaphore, DriverError> {
268 let create_info = vk::SemaphoreCreateInfo::default();
269 let allocation_callbacks = None;
270
271 unsafe { this.create_semaphore(&create_info, allocation_callbacks) }.map_err(|err| {
272 warn!("{err}");
273
274 DriverError::OutOfMemory
275 })
276 }
277
278 pub(crate) fn expect_accel_struct_ext(this: &Self) -> &khr::acceleration_structure::Device {
285 this.accel_struct_ext
286 .as_ref()
287 .expect("VK_KHR_acceleration_structure")
288 }
289
290 pub(crate) fn expect_surface_ext(this: &Self) -> &khr::surface::Instance {
296 this.surface_ext.as_ref().expect("VK_KHR_surface")
297 }
298
299 pub(crate) fn expect_swapchain_ext(this: &Self) -> &khr::swapchain::Device {
305 this.swapchain_ext.as_ref().expect("VK_KHR_swapchain")
306 }
307
308 #[profiling::function]
310 pub fn load(
311 instance: Instance,
312 physical_device: PhysicalDevice,
313 device: ash::Device,
314 display_window: bool,
315 ) -> Result<Self, DriverError> {
316 let debug = Instance::is_debug(&instance);
317 let allocator = Allocator::new(&AllocatorCreateDesc {
318 instance: (*instance).clone(),
319 device: device.clone(),
320 physical_device: *physical_device,
321 debug_settings: AllocatorDebugSettings {
322 log_leaks_on_shutdown: debug,
323 log_memory_information: debug,
324 log_allocations: debug,
325 ..Default::default()
326 },
327 buffer_device_address: true,
328 allocation_sizes: Default::default(),
329 })
330 .map_err(|err| {
331 warn!("{err}");
332
333 DriverError::Unsupported
334 })?;
335
336 let mut queues = Vec::with_capacity(physical_device.queue_families.len());
337
338 for (queue_family_index, properties) in physical_device.queue_families.iter().enumerate() {
339 let mut queue_family = Vec::with_capacity(properties.queue_count as _);
340
341 for queue_index in 0..properties.queue_count {
342 queue_family
343 .push(unsafe { device.get_device_queue(queue_family_index as _, queue_index) });
344 }
345
346 queues.push(queue_family);
347 }
348
349 let surface_ext = display_window
350 .then(|| khr::surface::Instance::new(Instance::entry(&instance), &instance));
351 let swapchain_ext = display_window.then(|| khr::swapchain::Device::new(&instance, &device));
352 let accel_struct_ext = physical_device
353 .accel_struct_properties
354 .is_some()
355 .then(|| khr::acceleration_structure::Device::new(&instance, &device));
356 let ray_trace_ext = physical_device
357 .ray_trace_features
358 .ray_tracing_pipeline
359 .then(|| khr::ray_tracing_pipeline::Device::new(&instance, &device));
360
361 let pipeline_cache =
362 unsafe { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None) }
363 .map_err(|err| {
364 warn!("{err}");
365
366 DriverError::Unsupported
367 })?;
368
369 Ok(Self {
370 accel_struct_ext,
371 allocator: ManuallyDrop::new(Mutex::new(allocator)),
372 device,
373 instance,
374 pipeline_cache,
375 physical_device,
376 queues,
377 ray_trace_ext,
378 surface_ext,
379 swapchain_ext,
380 })
381 }
382
383 #[profiling::function]
385 pub fn format_properties(this: &Self, format: vk::Format) -> vk::FormatProperties {
386 unsafe {
387 this.instance
388 .get_physical_device_format_properties(*this.physical_device, format)
389 }
390 }
391
392 #[profiling::function]
396 pub fn image_format_properties(
397 this: &Self,
398 format: vk::Format,
399 ty: vk::ImageType,
400 tiling: vk::ImageTiling,
401 usage: vk::ImageUsageFlags,
402 flags: vk::ImageCreateFlags,
403 ) -> Result<Option<vk::ImageFormatProperties>, DriverError> {
404 unsafe {
405 match this.instance.get_physical_device_image_format_properties(
406 *this.physical_device,
407 format,
408 ty,
409 tiling,
410 usage,
411 flags,
412 ) {
413 Ok(properties) => Ok(Some(properties)),
414 Err(err) if err == vk::Result::ERROR_FORMAT_NOT_SUPPORTED => {
415 Ok(None)
420 }
421 _ => Err(DriverError::OutOfMemory),
422 }
423 }
424 }
425
426 pub fn instance(this: &Self) -> &Instance {
428 &this.instance
429 }
430
431 pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache {
432 this.pipeline_cache
433 }
434
435 #[profiling::function]
436 pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> {
437 use std::slice::from_ref;
438
439 Device::wait_for_fences(this, from_ref(fence))
440 }
441
442 #[profiling::function]
443 pub(crate) fn wait_for_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
444 unsafe {
445 match this.device.wait_for_fences(fences, true, 100) {
446 Ok(_) => return Ok(()),
447 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
448 error!("Device lost");
449
450 return Err(DriverError::InvalidData);
451 }
452 Err(err) if err == vk::Result::TIMEOUT => {
453 trace!("waiting...");
454 }
455 _ => return Err(DriverError::OutOfMemory),
456 }
457
458 let started = Instant::now();
459
460 match this.device.wait_for_fences(fences, true, u64::MAX) {
461 Ok(_) => (),
462 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
463 error!("Device lost");
464
465 return Err(DriverError::InvalidData);
466 }
467 _ => return Err(DriverError::OutOfMemory),
468 }
469
470 let elapsed = Instant::now() - started;
471 let elapsed_millis = elapsed.as_millis();
472
473 if elapsed_millis > 0 {
474 warn!("waited for {} ms", elapsed_millis);
475 }
476 }
477
478 Ok(())
479 }
480}
481
482impl Debug for Device {
483 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
484 f.write_str("Device")
485 }
486}
487
488impl Deref for Device {
489 type Target = ash::Device;
490
491 fn deref(&self) -> &Self::Target {
492 &self.device
493 }
494}
495
496impl Drop for Device {
497 #[profiling::function]
498 fn drop(&mut self) {
499 if panicking() {
500 unsafe {
502 forget(ManuallyDrop::take(&mut self.allocator));
503 }
504
505 return;
506 }
507
508 if let Err(err) = unsafe { self.device.device_wait_idle() } {
511 warn!("device_wait_idle() failed: {err}");
512 }
513
514 unsafe {
515 self.device
516 .destroy_pipeline_cache(self.pipeline_cache, None);
517
518 ManuallyDrop::drop(&mut self.allocator);
519 }
520
521 unsafe {
522 self.device.destroy_device(None);
523 }
524 }
525}
526
527#[derive(Builder)]
529#[builder(
530 build_fn(private, name = "fallible_build", error = "DeviceInfoBuilderError"),
531 pattern = "owned"
532)]
533#[non_exhaustive]
534pub struct DeviceInfo {
535 #[builder(default)]
549 pub debug: bool,
550
551 #[builder(default = "Box::new(DeviceInfo::discrete_gpu)")]
554 pub select_physical_device: Box<SelectPhysicalDeviceFn>,
555}
556
557impl DeviceInfo {
558 #[allow(clippy::new_ret_no_self)]
560 #[deprecated = "Use DeviceInfo::default()"]
561 #[doc(hidden)]
562 pub fn new() -> DeviceInfoBuilder {
563 Default::default()
564 }
565
566 #[profiling::function]
569 pub fn integrated_gpu(physical_devices: &[PhysicalDevice]) -> usize {
570 assert!(!physical_devices.is_empty());
571
572 let mut physical_devices = physical_devices.iter().enumerate().collect::<Box<_>>();
573
574 if physical_devices.len() == 1 {
575 return 0;
576 }
577
578 fn device_type(ty: vk::PhysicalDeviceType) -> usize {
579 match ty {
580 vk::PhysicalDeviceType::INTEGRATED_GPU => 0,
581 vk::PhysicalDeviceType::VIRTUAL_GPU => 1,
582 vk::PhysicalDeviceType::CPU => 2,
583 vk::PhysicalDeviceType::DISCRETE_GPU => 3,
584 _ => 4,
585 }
586 }
587
588 physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| {
589 let lhs_device_ty = device_type(lhs.properties_v1_0.device_type);
590 let rhs_device_ty = device_type(rhs.properties_v1_0.device_type);
591 let device_ty = lhs_device_ty.cmp(&rhs_device_ty);
592
593 if device_ty != Ordering::Equal {
594 return device_ty;
595 }
596
597 Ordering::Equal
600 });
601
602 let (idx, _) = physical_devices[0];
603
604 idx
605 }
606
607 #[profiling::function]
610 pub fn discrete_gpu(physical_devices: &[PhysicalDevice]) -> usize {
611 assert!(!physical_devices.is_empty());
612
613 let mut physical_devices = physical_devices.iter().enumerate().collect::<Box<_>>();
614
615 if physical_devices.len() == 1 {
616 return 0;
617 }
618
619 fn device_type(ty: vk::PhysicalDeviceType) -> usize {
620 match ty {
621 vk::PhysicalDeviceType::DISCRETE_GPU => 0,
622 vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
623 vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
624 vk::PhysicalDeviceType::CPU => 3,
625 _ => 4,
626 }
627 }
628
629 physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| {
630 let lhs_device_ty = device_type(lhs.properties_v1_0.device_type);
631 let rhs_device_ty = device_type(rhs.properties_v1_0.device_type);
632 let device_ty = lhs_device_ty.cmp(&rhs_device_ty);
633
634 if device_ty != Ordering::Equal {
635 return device_ty;
636 }
637
638 Ordering::Equal
641 });
642
643 let (idx, _) = physical_devices[0];
644
645 idx
646 }
647
648 #[inline(always)]
650 pub fn to_builder(self) -> DeviceInfoBuilder {
651 DeviceInfoBuilder {
652 debug: Some(self.debug),
653 select_physical_device: Some(self.select_physical_device),
654 }
655 }
656}
657
658impl Debug for DeviceInfo {
659 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
660 f.debug_struct("DeviceInfo")
661 .field("debug", &self.debug)
662 .field("select_physical_device", &"fn")
663 .finish()
664 }
665}
666
667impl Default for DeviceInfo {
668 fn default() -> Self {
669 Self {
670 debug: false,
671 select_physical_device: Box::new(DeviceInfo::discrete_gpu),
672 }
673 }
674}
675
676impl From<DeviceInfoBuilder> for DeviceInfo {
677 fn from(info: DeviceInfoBuilder) -> Self {
678 info.build()
679 }
680}
681
682impl DeviceInfoBuilder {
683 #[inline(always)]
685 pub fn build(self) -> DeviceInfo {
686 let res = self.fallible_build();
687
688 #[cfg(test)]
689 let res = res.unwrap();
690
691 #[cfg(not(test))]
692 let res = unsafe { res.unwrap_unchecked() };
693
694 res
695 }
696}
697
698#[derive(Debug)]
699struct DeviceInfoBuilderError;
700
701impl From<UninitializedFieldError> for DeviceInfoBuilderError {
702 fn from(_: UninitializedFieldError) -> Self {
703 Self
704 }
705}
706
707#[cfg(test)]
708mod tests {
709 use super::*;
710
711 type Info = DeviceInfo;
712 type Builder = DeviceInfoBuilder;
713
714 #[test]
715 pub fn device_info() {
716 Info::default().to_builder().build();
717 }
718
719 #[test]
720 pub fn device_info_builder() {
721 Builder::default().build();
722 }
723}