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, info, trace, warn},
13 raw_window_handle::HasDisplayHandle,
14 std::{
15 cmp::Ordering,
16 ffi::CStr,
17 fmt::{Debug, Formatter},
18 iter::{empty, repeat_n},
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_n(
102 1.0,
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 info!("created {}", physical_device.properties_v1_0.device_name);
208
209 Self::load(instance, physical_device, device, display_window)
210 }
211
212 #[profiling::function]
214 pub fn create_headless(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
215 let DeviceInfo {
216 debug,
217 select_physical_device,
218 } = info.into();
219 let instance = Instance::create(debug, empty())?;
220
221 Self::create(instance, select_physical_device, false)
222 }
223
224 #[profiling::function]
226 pub fn create_display(
227 info: impl Into<DeviceInfo>,
228 display_handle: &impl HasDisplayHandle,
229 ) -> Result<Self, DriverError> {
230 let DeviceInfo {
231 debug,
232 select_physical_device,
233 } = info.into();
234 let display_handle = display_handle.display_handle().map_err(|err| {
235 warn!("{err}");
236
237 DriverError::Unsupported
238 })?;
239 let required_extensions = enumerate_required_extensions(display_handle.as_raw())
240 .map_err(|err| {
241 warn!("{err}");
242
243 DriverError::Unsupported
244 })?
245 .iter()
246 .map(|ext| unsafe { CStr::from_ptr(*ext as *const _) });
247 let instance = Instance::create(debug, required_extensions)?;
248
249 Self::create(instance, select_physical_device, true)
250 }
251
252 pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result<vk::Fence, DriverError> {
253 let mut flags = vk::FenceCreateFlags::empty();
254
255 if signaled {
256 flags |= vk::FenceCreateFlags::SIGNALED;
257 }
258
259 let create_info = vk::FenceCreateInfo::default().flags(flags);
260 let allocation_callbacks = None;
261
262 unsafe { this.create_fence(&create_info, allocation_callbacks) }.map_err(|err| {
263 warn!("{err}");
264
265 DriverError::OutOfMemory
266 })
267 }
268
269 pub(crate) fn create_semaphore(this: &Self) -> Result<vk::Semaphore, DriverError> {
270 let create_info = vk::SemaphoreCreateInfo::default();
271 let allocation_callbacks = None;
272
273 unsafe { this.create_semaphore(&create_info, allocation_callbacks) }.map_err(|err| {
274 warn!("{err}");
275
276 DriverError::OutOfMemory
277 })
278 }
279
280 pub(crate) fn expect_accel_struct_ext(this: &Self) -> &khr::acceleration_structure::Device {
287 this.accel_struct_ext
288 .as_ref()
289 .expect("VK_KHR_acceleration_structure")
290 }
291
292 pub(crate) fn expect_surface_ext(this: &Self) -> &khr::surface::Instance {
298 this.surface_ext.as_ref().expect("VK_KHR_surface")
299 }
300
301 pub(crate) fn expect_swapchain_ext(this: &Self) -> &khr::swapchain::Device {
307 this.swapchain_ext.as_ref().expect("VK_KHR_swapchain")
308 }
309
310 #[profiling::function]
312 pub fn load(
313 instance: Instance,
314 physical_device: PhysicalDevice,
315 device: ash::Device,
316 display_window: bool,
317 ) -> Result<Self, DriverError> {
318 let debug = Instance::is_debug(&instance);
319 let mut debug_settings = AllocatorDebugSettings::default();
320 debug_settings.log_leaks_on_shutdown = debug;
321 debug_settings.log_memory_information = debug;
322 debug_settings.log_allocations = debug;
323
324 let allocator = Allocator::new(&AllocatorCreateDesc {
325 instance: (*instance).clone(),
326 device: device.clone(),
327 physical_device: *physical_device,
328 debug_settings,
329 buffer_device_address: true,
330 allocation_sizes: Default::default(),
331 })
332 .map_err(|err| {
333 warn!("{err}");
334
335 DriverError::Unsupported
336 })?;
337
338 let mut queues = Vec::with_capacity(physical_device.queue_families.len());
339
340 for (queue_family_index, properties) in physical_device.queue_families.iter().enumerate() {
341 let mut queue_family = Vec::with_capacity(properties.queue_count as _);
342
343 for queue_index in 0..properties.queue_count {
344 queue_family
345 .push(unsafe { device.get_device_queue(queue_family_index as _, queue_index) });
346 }
347
348 queues.push(queue_family);
349 }
350
351 let surface_ext = display_window
352 .then(|| khr::surface::Instance::new(Instance::entry(&instance), &instance));
353 let swapchain_ext = display_window.then(|| khr::swapchain::Device::new(&instance, &device));
354 let accel_struct_ext = physical_device
355 .accel_struct_properties
356 .is_some()
357 .then(|| khr::acceleration_structure::Device::new(&instance, &device));
358 let ray_trace_ext = physical_device
359 .ray_trace_features
360 .ray_tracing_pipeline
361 .then(|| khr::ray_tracing_pipeline::Device::new(&instance, &device));
362
363 let pipeline_cache =
364 unsafe { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None) }
365 .map_err(|err| {
366 warn!("{err}");
367
368 DriverError::Unsupported
369 })?;
370
371 Ok(Self {
372 accel_struct_ext,
373 allocator: ManuallyDrop::new(Mutex::new(allocator)),
374 device,
375 instance,
376 pipeline_cache,
377 physical_device,
378 queues,
379 ray_trace_ext,
380 surface_ext,
381 swapchain_ext,
382 })
383 }
384
385 #[profiling::function]
387 pub fn format_properties(this: &Self, format: vk::Format) -> vk::FormatProperties {
388 unsafe {
389 this.instance
390 .get_physical_device_format_properties(*this.physical_device, format)
391 }
392 }
393
394 #[profiling::function]
398 pub fn image_format_properties(
399 this: &Self,
400 format: vk::Format,
401 ty: vk::ImageType,
402 tiling: vk::ImageTiling,
403 usage: vk::ImageUsageFlags,
404 flags: vk::ImageCreateFlags,
405 ) -> Result<Option<vk::ImageFormatProperties>, DriverError> {
406 unsafe {
407 match this.instance.get_physical_device_image_format_properties(
408 *this.physical_device,
409 format,
410 ty,
411 tiling,
412 usage,
413 flags,
414 ) {
415 Ok(properties) => Ok(Some(properties)),
416 Err(err) if err == vk::Result::ERROR_FORMAT_NOT_SUPPORTED => {
417 Ok(None)
422 }
423 _ => Err(DriverError::OutOfMemory),
424 }
425 }
426 }
427
428 pub fn instance(this: &Self) -> &Instance {
430 &this.instance
431 }
432
433 pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache {
434 this.pipeline_cache
435 }
436
437 #[profiling::function]
438 pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> {
439 use std::slice::from_ref;
440
441 Device::wait_for_fences(this, from_ref(fence))
442 }
443
444 #[profiling::function]
445 pub(crate) fn wait_for_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
446 unsafe {
447 match this.device.wait_for_fences(fences, true, 100) {
448 Ok(_) => return Ok(()),
449 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
450 error!("Device lost");
451
452 return Err(DriverError::InvalidData);
453 }
454 Err(err) if err == vk::Result::TIMEOUT => {
455 trace!("waiting...");
456 }
457 _ => return Err(DriverError::OutOfMemory),
458 }
459
460 let started = Instant::now();
461
462 match this.device.wait_for_fences(fences, true, u64::MAX) {
463 Ok(_) => (),
464 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
465 error!("Device lost");
466
467 return Err(DriverError::InvalidData);
468 }
469 _ => return Err(DriverError::OutOfMemory),
470 }
471
472 let elapsed = Instant::now() - started;
473 let elapsed_millis = elapsed.as_millis();
474
475 if elapsed_millis > 0 {
476 warn!("waited for {} ms", elapsed_millis);
477 }
478 }
479
480 Ok(())
481 }
482}
483
484impl Debug for Device {
485 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
486 f.write_str("Device")
487 }
488}
489
490impl Deref for Device {
491 type Target = ash::Device;
492
493 fn deref(&self) -> &Self::Target {
494 &self.device
495 }
496}
497
498impl Drop for Device {
499 #[profiling::function]
500 fn drop(&mut self) {
501 if panicking() {
502 unsafe {
504 forget(ManuallyDrop::take(&mut self.allocator));
505 }
506
507 return;
508 }
509
510 if let Err(err) = unsafe { self.device.device_wait_idle() } {
513 warn!("device_wait_idle() failed: {err}");
514 }
515
516 unsafe {
517 self.device
518 .destroy_pipeline_cache(self.pipeline_cache, None);
519
520 ManuallyDrop::drop(&mut self.allocator);
521 }
522
523 unsafe {
524 self.device.destroy_device(None);
525 }
526 }
527}
528
529#[derive(Builder)]
531#[builder(
532 build_fn(private, name = "fallible_build", error = "DeviceInfoBuilderError"),
533 pattern = "owned"
534)]
535#[non_exhaustive]
536pub struct DeviceInfo {
537 #[builder(default)]
551 pub debug: bool,
552
553 #[builder(default = "Box::new(DeviceInfo::discrete_gpu)")]
556 pub select_physical_device: Box<SelectPhysicalDeviceFn>,
557}
558
559impl DeviceInfo {
560 #[allow(clippy::new_ret_no_self)]
562 #[deprecated = "Use DeviceInfo::default()"]
563 #[doc(hidden)]
564 pub fn new() -> DeviceInfoBuilder {
565 Default::default()
566 }
567
568 #[profiling::function]
571 pub fn integrated_gpu(physical_devices: &[PhysicalDevice]) -> usize {
572 assert!(!physical_devices.is_empty());
573
574 let mut physical_devices = physical_devices.iter().enumerate().collect::<Box<_>>();
575
576 if physical_devices.len() == 1 {
577 return 0;
578 }
579
580 fn device_type(ty: vk::PhysicalDeviceType) -> usize {
581 match ty {
582 vk::PhysicalDeviceType::INTEGRATED_GPU => 0,
583 vk::PhysicalDeviceType::VIRTUAL_GPU => 1,
584 vk::PhysicalDeviceType::CPU => 2,
585 vk::PhysicalDeviceType::DISCRETE_GPU => 3,
586 _ => 4,
587 }
588 }
589
590 physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| {
591 let lhs_device_ty = device_type(lhs.properties_v1_0.device_type);
592 let rhs_device_ty = device_type(rhs.properties_v1_0.device_type);
593 let device_ty = lhs_device_ty.cmp(&rhs_device_ty);
594
595 if device_ty != Ordering::Equal {
596 return device_ty;
597 }
598
599 Ordering::Equal
602 });
603
604 let (idx, _) = physical_devices[0];
605
606 idx
607 }
608
609 #[profiling::function]
612 pub fn discrete_gpu(physical_devices: &[PhysicalDevice]) -> usize {
613 assert!(!physical_devices.is_empty());
614
615 let mut physical_devices = physical_devices.iter().enumerate().collect::<Box<_>>();
616
617 if physical_devices.len() == 1 {
618 return 0;
619 }
620
621 fn device_type(ty: vk::PhysicalDeviceType) -> usize {
622 match ty {
623 vk::PhysicalDeviceType::DISCRETE_GPU => 0,
624 vk::PhysicalDeviceType::INTEGRATED_GPU => 1,
625 vk::PhysicalDeviceType::VIRTUAL_GPU => 2,
626 vk::PhysicalDeviceType::CPU => 3,
627 _ => 4,
628 }
629 }
630
631 physical_devices.sort_unstable_by(|(_, lhs), (_, rhs)| {
632 let lhs_device_ty = device_type(lhs.properties_v1_0.device_type);
633 let rhs_device_ty = device_type(rhs.properties_v1_0.device_type);
634 let device_ty = lhs_device_ty.cmp(&rhs_device_ty);
635
636 if device_ty != Ordering::Equal {
637 return device_ty;
638 }
639
640 Ordering::Equal
643 });
644
645 let (idx, _) = physical_devices[0];
646
647 idx
648 }
649
650 #[inline(always)]
652 pub fn to_builder(self) -> DeviceInfoBuilder {
653 DeviceInfoBuilder {
654 debug: Some(self.debug),
655 select_physical_device: Some(self.select_physical_device),
656 }
657 }
658}
659
660impl Debug for DeviceInfo {
661 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
662 f.debug_struct("DeviceInfo")
663 .field("debug", &self.debug)
664 .field("select_physical_device", &"fn")
665 .finish()
666 }
667}
668
669impl Default for DeviceInfo {
670 fn default() -> Self {
671 Self {
672 debug: false,
673 select_physical_device: Box::new(DeviceInfo::discrete_gpu),
674 }
675 }
676}
677
678impl From<DeviceInfoBuilder> for DeviceInfo {
679 fn from(info: DeviceInfoBuilder) -> Self {
680 info.build()
681 }
682}
683
684impl DeviceInfoBuilder {
685 #[inline(always)]
687 pub fn build(self) -> DeviceInfo {
688 let res = self.fallible_build();
689
690 #[cfg(test)]
691 let res = res.unwrap();
692
693 #[cfg(not(test))]
694 let res = unsafe { res.unwrap_unchecked() };
695
696 res
697 }
698}
699
700#[derive(Debug)]
701struct DeviceInfoBuilderError;
702
703impl From<UninitializedFieldError> for DeviceInfoBuilderError {
704 fn from(_: UninitializedFieldError) -> Self {
705 Self
706 }
707}
708
709#[cfg(test)]
710mod tests {
711 use super::*;
712
713 type Info = DeviceInfo;
714 type Builder = DeviceInfoBuilder;
715
716 #[test]
717 pub fn device_info() {
718 Info::default().to_builder().build();
719 }
720
721 #[test]
722 pub fn device_info_builder() {
723 Builder::default().build();
724 }
725}