Skip to main content

vk_graph/driver/
device.rs

1//! Logical device types
2
3use {
4    super::{
5        DriverError,
6        instance::{ApiVersion, Instance, InstanceInfoBuilder},
7        physical_device::PhysicalDevice,
8    },
9    ash::{ext, khr, vk},
10    derive_builder::Builder,
11    gpu_allocator::{
12        AllocatorDebugSettings,
13        vulkan::{Allocator, AllocatorCreateDesc},
14    },
15    log::{error, info, trace, warn},
16    raw_window_handle::HasDisplayHandle,
17    std::{
18        collections::HashMap,
19        ffi::CString,
20        fmt::{Debug, Formatter},
21        mem::{ManuallyDrop, forget},
22        ops::Deref,
23        slice,
24        sync::Arc,
25        sync::atomic::{AtomicU64, Ordering},
26        thread::panicking,
27        time::Instant,
28    },
29};
30
31#[cfg(feature = "parking_lot")]
32use parking_lot::Mutex;
33
34#[cfg(not(feature = "parking_lot"))]
35use std::sync::Mutex;
36
37fn select_physical_device(
38    instance: &Instance,
39    mut index: usize,
40) -> Result<PhysicalDevice, DriverError> {
41    let mut physical_devices = Instance::physical_devices(instance)?
42        .into_iter()
43        .collect::<Vec<_>>();
44    if physical_devices.is_empty() {
45        warn!("unable to find physical devices");
46
47        return Err(DriverError::Unsupported);
48    }
49
50    if index >= physical_devices.len() {
51        index = 0;
52    }
53
54    let physical_device = physical_devices.remove(index);
55
56    Ok(physical_device)
57}
58
59/// Opaque handle to a device object.
60#[read_only::embed]
61#[derive(Clone)]
62pub struct Device {
63    #[readonly]
64    pub(self) inner: Arc<DeviceInner>,
65
66    /// The selected physical device.
67    ///
68    /// This contains the physical device's advertised features, properties, limits, memory
69    /// properties, queue families, and Vulkan extension support. Extension support is exposed with
70    /// fields named after the Vulkan extension, for example
71    /// `device.physical.vk_khr_acceleration_structure.is_some()`.
72    ///
73    /// _Note:_ This field is read-only.
74    #[readonly]
75    pub physical: Box<PhysicalDevice>,
76}
77
78impl Device {
79    /// Begins recording a command buffer on this device.
80    ///
81    /// This is a thin wrapper around [`ash::Device::begin_command_buffer`] that maps Vulkan errors
82    /// to [`DriverError`] variants.
83    pub fn begin_command_buffer(
84        this: &Self,
85        cmd: vk::CommandBuffer,
86        begin_info: &vk::CommandBufferBeginInfo,
87    ) -> Result<(), DriverError> {
88        unsafe {
89            this.begin_command_buffer(cmd, begin_info).map_err(|err| {
90                warn!("unable to begin command buffer: {err}");
91
92                match err {
93                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
94                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
95                    _ => DriverError::Unsupported,
96                }
97            })
98        }
99    }
100
101    /// Begins a Vulkan debug label region on `command_buffer` when debug labeling is enabled.
102    ///
103    /// Returns without doing any work if debug mode is `false`.
104    pub fn begin_debug_utils_label(
105        this: &Self,
106        command_buffer: vk::CommandBuffer,
107        label_name: impl AsRef<str>,
108    ) -> Result<(), DriverError> {
109        if !this.physical.instance.info.debug {
110            return Ok(());
111        }
112
113        let Ok(label_name) = CString::new(label_name.as_ref()) else {
114            warn!("invalid label name");
115
116            return Err(DriverError::InvalidData);
117        };
118
119        let ext = Self::try_vk_ext_debug_utils(this)?;
120
121        unsafe {
122            ext.cmd_begin_debug_utils_label(
123                command_buffer,
124                &vk::DebugUtilsLabelEXT::default().label_name(label_name.as_c_str()),
125            );
126        }
127
128        Ok(())
129    }
130
131    /// Clears Vulkan private-data metadata associated with `object_type` and `object_handle`.
132    pub(crate) fn clear_private_data_object_name<T>(
133        this: &Self,
134        object_type: vk::ObjectType,
135        object_handle: T,
136    ) -> Result<(), DriverError>
137    where
138        T: vk::Handle + Copy,
139    {
140        if this.inner.private_data_slot.is_none() {
141            return Ok(());
142        }
143
144        if object_handle.is_null() {
145            warn!("invalid object handle");
146
147            return Err(DriverError::InvalidData);
148        }
149
150        let object_key = (object_type, object_handle.as_raw());
151        let previous_metadata_id = Self::with_object_metadata_ids(this, |object_to_metadata_id| {
152            object_to_metadata_id.remove(&object_key)
153        });
154
155        if previous_metadata_id.is_none() {
156            return Ok(());
157        }
158
159        let ext = Self::try_vk_ext_private_data(this)?;
160        let private_data_slot = this
161            .inner
162            .private_data_slot
163            .expect("missing private data slot");
164
165        if let Err(err) = unsafe { ext.set_private_data(object_handle, private_data_slot, 0) } {
166            Self::with_object_metadata_ids(this, |object_metadata_ids| {
167                if let Some(metadata_id) = previous_metadata_id {
168                    object_metadata_ids.insert(object_key, metadata_id);
169                }
170            });
171
172            warn!("unable to clear private data object name: {err}");
173
174            return Err(match err {
175                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
176                    DriverError::OutOfMemory
177                }
178                _ => DriverError::Unsupported,
179            });
180        }
181
182        Self::with_private_data_metadata(this, |metadata| {
183            metadata
184                .names
185                .remove(&previous_metadata_id.expect("metadata id removed"));
186        });
187
188        Ok(())
189    }
190
191    /// Records a pipeline barrier using the `VK_KHR_synchronization2` extension or the Vulkan 1.3
192    /// core `vkCmdPipelineBarrier2` path.
193    ///
194    /// See [`vkCmdPipelineBarrier2`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCmdPipelineBarrier2.html)
195    /// and [`VK_KHR_synchronization2`](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_synchronization2.html).
196    pub fn cmd_pipeline_barrier2(
197        this: &Self,
198        command_buffer: vk::CommandBuffer,
199        dependency_info: &vk::DependencyInfo,
200    ) {
201        #[cfg(feature = "checked")]
202        assert!(
203            this.physical.vk_khr_synchronization2,
204            "missing synchronization2 feature"
205        );
206
207        unsafe {
208            if this.physical.instance.info.api_version >= ApiVersion::Vulkan13 {
209                this.cmd_pipeline_barrier2(command_buffer, dependency_info);
210            } else {
211                let khr_synchronization2 = Device::expect_vk_khr_synchronization2(this);
212
213                khr_synchronization2.cmd_pipeline_barrier2(command_buffer, dependency_info);
214            }
215        }
216    }
217
218    /// Constructs a new device using the given configuration.
219    ///
220    /// This constructor is intended for headless or manually managed setups. It does not infer or
221    /// enable display platform surface extensions. Use [`Self::try_from_display`] when the
222    /// resulting device must be capable of later surface creation.
223    #[profiling::function]
224    pub fn create(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
225        let DeviceInfo {
226            debug,
227            physical_device_index,
228        } = info.into();
229        let instance_info = InstanceInfoBuilder::default().debug(debug);
230        let instance = Instance::create(instance_info)?;
231        let physical_device = select_physical_device(&instance, physical_device_index)?;
232
233        Self::try_from_physical_device(physical_device)
234    }
235
236    /// Creates a Vulkan fence on this device.
237    ///
238    /// Pass `true` for `signaled` when the fence should begin in the signaled state.
239    ///
240    /// See [`vkCreateFence`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCreateFence.html).
241    pub fn create_fence(this: &Self, signaled: bool) -> Result<vk::Fence, DriverError> {
242        let mut flags = vk::FenceCreateFlags::empty();
243
244        if signaled {
245            flags |= vk::FenceCreateFlags::SIGNALED;
246        }
247
248        let create_info = vk::FenceCreateInfo::default().flags(flags);
249        let allocation_callbacks = None;
250
251        unsafe {
252            this.create_fence(&create_info, allocation_callbacks)
253                .map_err(|err| {
254                    warn!("unable to create fence: {err}");
255
256                    DriverError::OutOfMemory
257                })
258        }
259    }
260
261    /// Creates a Vulkan binary semaphore on this device.
262    ///
263    /// See [`vkCreateSemaphore`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkCreateSemaphore.html).
264    pub fn create_semaphore(this: &Self) -> Result<vk::Semaphore, DriverError> {
265        let create_info = vk::SemaphoreCreateInfo::default();
266        let allocation_callbacks = None;
267
268        unsafe {
269            this.create_semaphore(&create_info, allocation_callbacks)
270                .map_err(|err| {
271                    warn!("unable to create semaphore: {err}");
272
273                    DriverError::OutOfMemory
274                })
275        }
276    }
277
278    /// Ends recording a command buffer on this device.
279    ///
280    /// This is a thin wrapper around [`ash::Device::end_command_buffer`] that maps Vulkan errors
281    /// to [`DriverError`] variants.
282    ///
283    /// See [`vkEndCommandBuffer`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkEndCommandBuffer.html).
284    pub fn end_command_buffer(this: &Self, cmd: vk::CommandBuffer) -> Result<(), DriverError> {
285        unsafe {
286            this.end_command_buffer(cmd).map_err(|err| {
287                warn!("unable to end command buffer: {err}");
288
289                match err {
290                    vk::Result::ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR => DriverError::InvalidData,
291                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
292                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
293                    _ => DriverError::Unsupported,
294                }
295            })
296        }
297    }
298
299    /// Ends a Vulkan debug label region on `command_buffer` when debug labeling is enabled.
300    ///
301    /// Returns without doing any work if debug mode is `false`.
302    pub fn end_debug_utils_label(
303        this: &Self,
304        command_buffer: vk::CommandBuffer,
305    ) -> Result<(), DriverError> {
306        if !this.physical.instance.info.debug {
307            return Ok(());
308        }
309
310        let ext = Self::try_vk_ext_debug_utils(this)?;
311
312        unsafe {
313            ext.cmd_end_debug_utils_label(command_buffer);
314        }
315
316        Ok(())
317    }
318
319    /// Helper for times when you already know that the device supports the acceleration
320    /// structure extension.
321    ///
322    /// # Panics
323    ///
324    /// Panics if acceleration structure support was not enabled for this device.
325    pub(crate) fn expect_vk_khr_acceleration_structure(
326        this: &Self,
327    ) -> &khr::acceleration_structure::Device {
328        this.inner
329            .vk_khr_acceleration_structure
330            .as_ref()
331            .expect("missing VK_KHR_acceleration_structure")
332    }
333
334    /// Helper for times when you already know that present wait support is enabled.
335    ///
336    /// # Panics
337    ///
338    /// Panics if `VK_KHR_present_wait` support was not enabled for this device.
339    pub(crate) fn expect_vk_khr_present_wait(this: &Self) -> &khr::present_wait::Device {
340        this.inner
341            .vk_khr_present_wait
342            .as_ref()
343            .expect("missing VK_KHR_present_wait")
344    }
345
346    /// Helper for times when you already know that the device supports the ray tracing pipeline
347    /// extension.
348    ///
349    /// # Panics
350    ///
351    /// Panics if ray tracing pipeline support was not enabled for this device.
352    pub(crate) fn expect_vk_khr_ray_tracing_pipeline(
353        this: &Self,
354    ) -> &khr::ray_tracing_pipeline::Device {
355        this.inner
356            .vk_khr_ray_tracing_pipeline
357            .as_ref()
358            .expect("missing VK_KHR_ray_tracing_pipeline")
359    }
360
361    /// Helper for times when you already know that the instance supports the surface extension.
362    ///
363    /// # Panics
364    ///
365    /// Panics if the device was not created for display window access.
366    pub(crate) fn expect_vk_khr_surface(this: &Self) -> &khr::surface::Instance {
367        this.inner
368            .vk_khr_surface
369            .as_ref()
370            .expect("missing VK_KHR_surface")
371    }
372
373    /// Helper for times when you already know that the device supports the synchronization2
374    /// extension.
375    ///
376    /// # Panics
377    ///
378    /// Panics if `VK_KHR_synchronization2` is not available.
379    pub(crate) fn expect_vk_khr_synchronization2(this: &Self) -> &khr::synchronization2::Device {
380        this.inner
381            .vk_khr_synchronization2
382            .as_ref()
383            .expect("missing VK_KHR_synchronization2")
384    }
385
386    /// Helper for times when you already know that the device supports the swapchain extension.
387    ///
388    /// # Panics
389    ///
390    /// Panics if the device was not created for display window access.
391    pub(crate) fn expect_vk_khr_swapchain(this: &Self) -> &khr::swapchain::Device {
392        this.inner
393            .vk_khr_swapchain
394            .as_ref()
395            .expect("missing VK_KHR_swapchain")
396    }
397
398    /// Removes local Vulkan private-data metadata without touching the Vulkan object.
399    pub(crate) fn forget_private_data_object_name<T>(
400        this: &Self,
401        object_type: vk::ObjectType,
402        object_handle: T,
403    ) where
404        T: vk::Handle + Copy,
405    {
406        if this.inner.private_data_slot.is_none() || object_handle.is_null() {
407            return;
408        }
409
410        let object_key = (object_type, object_handle.as_raw());
411        let Some(metadata_id) = Self::with_object_metadata_ids(this, |object_metadata_ids| {
412            object_metadata_ids.remove(&object_key)
413        }) else {
414            return;
415        };
416
417        Self::with_private_data_metadata(this, |metadata| {
418            metadata.names.remove(&metadata_id);
419        });
420    }
421
422    /// Returns `true` if both handles refer to the same logical device allocation.
423    pub(crate) fn is_same(lhs: &Self, rhs: &Self) -> bool {
424        Arc::ptr_eq(&lhs.inner, &rhs.inner)
425    }
426
427    /// Returns the device-owned pipeline cache handle.
428    pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache {
429        this.inner.pipeline_cache
430    }
431
432    /// Retrieves a Vulkan private-data name associated with `handle`.
433    ///
434    /// Returns `None` when metadata is not available or when the `VK_EXT_private_data` extension is
435    /// not available.
436    pub(crate) fn private_data_object_name<T>(
437        this: &Self,
438        object_type: vk::ObjectType,
439        object_handle: T,
440    ) -> Option<String>
441    where
442        T: vk::Handle + Copy,
443    {
444        this.inner.private_data_slot?;
445
446        if object_handle.is_null() {
447            return None;
448        }
449
450        let object_key = (object_type, object_handle.as_raw());
451        Self::with_private_data_metadata(this, |metadata| {
452            let metadata_id = metadata.object_metadata_ids.get(&object_key)?;
453
454            metadata.names.get(metadata_id).cloned()
455        })
456    }
457
458    /// Submits command buffers to a queue, optionally signaling a fence.
459    ///
460    /// See [`vkQueueSubmit`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkQueueSubmit.html).
461    pub fn queue_submit(
462        this: &Self,
463        queue: vk::Queue,
464        submits: &[vk::SubmitInfo],
465        fence: vk::Fence,
466    ) -> Result<(), DriverError> {
467        unsafe {
468            this.queue_submit(queue, submits, fence).map_err(|err| {
469                warn!("unable to queue submits: {err}");
470
471                match err {
472                    vk::Result::ERROR_DEVICE_LOST => DriverError::InvalidData,
473                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
474                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
475                    _ => DriverError::Unsupported,
476                }
477            })
478        }
479    }
480
481    /// Submits command buffers to a queue using the `VK_KHR_synchronization2` extension or the
482    /// Vulkan 1.3 core `vkQueueSubmit2` path.
483    ///
484    /// See [`vkQueueSubmit2`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkQueueSubmit2.html)
485    /// and [`VK_KHR_synchronization2`](https://registry.khronos.org/vulkan/specs/latest/man/html/VK_KHR_synchronization2.html).
486    pub fn queue_submit2(
487        this: &Self,
488        queue: vk::Queue,
489        submits: &[vk::SubmitInfo2],
490        fence: vk::Fence,
491    ) -> Result<(), DriverError> {
492        #[cfg(feature = "checked")]
493        assert!(this.physical.vk_khr_synchronization2);
494
495        unsafe {
496            if this.physical.instance.info.api_version >= ApiVersion::Vulkan13 {
497                // Support derived from Vulkan v1.3 implementation
498                this.queue_submit2(queue, submits, fence)
499            } else {
500                let khr_synchronization2 = Device::expect_vk_khr_synchronization2(this);
501
502                // Support derived from Vulkan v1.2 implementation + extension
503                khr_synchronization2.queue_submit2(queue, submits, fence)
504            }
505            .map_err(|err| {
506                warn!("unable to queue submit2 submissions: {err}");
507
508                match err {
509                    vk::Result::ERROR_DEVICE_LOST => DriverError::InvalidData,
510                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
511                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
512                    _ => DriverError::Unsupported,
513                }
514            })
515        }
516    }
517
518    /// Waits for queue idle.
519    pub fn queue_wait_idle(this: &Self, queue: vk::Queue) -> Result<(), DriverError> {
520        unsafe {
521            this.queue_wait_idle(queue).map_err(|err| {
522                warn!("unable to wait for queue idle: {err}");
523
524                match err {
525                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
526                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
527                    vk::Result::ERROR_DEVICE_LOST | vk::Result::ERROR_VALIDATION_FAILED_EXT => {
528                        DriverError::InvalidData
529                    }
530                    _ => DriverError::Unsupported,
531                }
532            })
533        }
534    }
535
536    /// Resets one or more fences to the unsignaled state.
537    ///
538    /// See [`vkResetFences`](https://registry.khronos.org/vulkan/specs/latest/man/html/vkResetFences.html).
539    pub fn reset_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
540        unsafe {
541            this.reset_fences(fences).map_err(|err| {
542                warn!("unable to reset fences: {err}");
543
544                match err {
545                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => DriverError::OutOfMemory,
546                    _ => DriverError::Unsupported,
547                }
548            })
549        }
550    }
551
552    /// Assigns a Vulkan debug-utils name to `handle` when debug labeling is enabled.
553    ///
554    /// Returns without doing any work if debug mode is `false`.
555    pub fn set_debug_utils_object_name<T>(
556        this: &Self,
557        object_handle: T,
558        object_name: impl AsRef<str>,
559    ) -> Result<(), DriverError>
560    where
561        T: vk::Handle + Copy,
562    {
563        if !this.physical.instance.info.debug {
564            return Ok(());
565        }
566
567        if object_handle.is_null() {
568            warn!("invalid object handle");
569
570            return Err(DriverError::InvalidData);
571        }
572
573        let Ok(object_name) = CString::new(object_name.as_ref()) else {
574            warn!("invalid object name");
575
576            return Err(DriverError::InvalidData);
577        };
578
579        let ext = Self::try_vk_ext_debug_utils(this)?;
580
581        unsafe {
582            match ext.set_debug_utils_object_name(
583                &vk::DebugUtilsObjectNameInfoEXT::default()
584                    .object_handle(object_handle)
585                    .object_name(object_name.as_c_str()),
586            ) {
587                Err(
588                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY,
589                ) => Err(DriverError::OutOfMemory),
590                Err(vk::Result::ERROR_VALIDATION_FAILED_EXT) => Err(DriverError::InvalidData),
591                Err(err) => {
592                    warn!("unable to set debug utils object name: {err}");
593
594                    Err(DriverError::Unsupported)
595                }
596                Ok(_) => Ok(()),
597            }
598        }
599    }
600
601    /// Stores a Vulkan name in private-data metadata.
602    ///
603    /// Returns without doing any work when `VK_EXT_private_data` is not available.
604    pub(crate) fn set_private_data_object_name<T>(
605        this: &Self,
606        object_type: vk::ObjectType,
607        object_handle: T,
608        object_name: impl AsRef<str>,
609    ) -> Result<(), DriverError>
610    where
611        T: vk::Handle + Copy,
612    {
613        if this.inner.private_data_slot.is_none() {
614            return Ok(());
615        }
616
617        if object_handle.is_null() {
618            warn!("invalid object handle");
619
620            return Err(DriverError::InvalidData);
621        }
622
623        let object_key = (object_type, object_handle.as_raw());
624        let metadata_id = this
625            .inner
626            .private_data_name_id
627            .fetch_add(1, Ordering::Relaxed)
628            + 1;
629
630        let (previous_metadata_id, previous_name) =
631            Self::with_private_data_metadata(this, |metadata| {
632                let previous_metadata_id =
633                    metadata.object_metadata_ids.insert(object_key, metadata_id);
634                let previous_name = previous_metadata_id.and_then(|id| metadata.names.remove(&id));
635
636                metadata
637                    .names
638                    .insert(metadata_id, object_name.as_ref().to_owned());
639
640                (previous_metadata_id, previous_name)
641            });
642
643        let ext = Self::try_vk_ext_private_data(this)?;
644        let private_data_slot = this
645            .inner
646            .private_data_slot
647            .expect("missing private data slot");
648
649        if let Err(err) =
650            unsafe { ext.set_private_data(object_handle, private_data_slot, metadata_id) }
651        {
652            Self::with_private_data_metadata(this, |metadata| {
653                let _ = metadata.names.remove(&metadata_id);
654                match previous_metadata_id {
655                    Some(id) => {
656                        metadata.object_metadata_ids.insert(object_key, id);
657                        if let Some(name) = previous_name {
658                            metadata.names.insert(id, name);
659                        }
660                    }
661                    None => {
662                        metadata.object_metadata_ids.remove(&object_key);
663                    }
664                }
665            });
666
667            warn!("unable to set private data object name: {err}");
668
669            return Err(match err {
670                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY | vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
671                    DriverError::OutOfMemory
672                }
673                _ => DriverError::Unsupported,
674            });
675        }
676
677        Ok(())
678    }
679
680    /// Loads an existing `ash` Vulkan device that may have been created by other means.
681    ///
682    /// # Safety
683    ///
684    /// `device` must have been created from `physical_device` and must have all queues,
685    /// extensions, and features expected by `vk-graph`. The device must not be destroyed outside
686    /// this wrapper while any cloned [`Device`] or resources created from it remain alive.
687    #[profiling::function]
688    pub unsafe fn try_from_ash(
689        device: ash::Device,
690        physical_device: PhysicalDevice,
691    ) -> Result<Self, DriverError> {
692        let debug = physical_device.instance.info.debug;
693
694        if debug && !Instance::supports_debug_utils(&physical_device.instance) {
695            error!("unsupported VK_EXT_debug_utils");
696
697            return Err(DriverError::Unsupported);
698        }
699
700        if debug && !physical_device.vk_ext_private_data {
701            error!("unsupported VK_EXT_private_data");
702
703            return Err(DriverError::Unsupported);
704        }
705
706        let mut debug_settings = AllocatorDebugSettings::default();
707        debug_settings.log_leaks_on_shutdown = debug;
708        debug_settings.log_memory_information = debug;
709        debug_settings.log_allocations = debug;
710
711        let allocator = Allocator::new(&AllocatorCreateDesc {
712            instance: (*physical_device.instance).clone(),
713            device: device.clone(),
714            physical_device: physical_device.handle,
715            debug_settings,
716            buffer_device_address: true,
717            allocation_sizes: Default::default(),
718        })
719        .map_err(|err| {
720            warn!("unable to create allocator: {err}");
721
722            DriverError::Unsupported
723        })?;
724
725        let mut queues = Vec::with_capacity(physical_device.queue_families.len());
726
727        for (queue_family_index, properties) in physical_device.queue_families.iter().enumerate() {
728            let mut queue_family = Vec::with_capacity(properties.queue_count as _);
729
730            for queue_index in 0..properties.queue_count {
731                queue_family.push(Mutex::new(unsafe {
732                    device.get_device_queue(queue_family_index as _, queue_index)
733                }));
734            }
735
736            queues.push(queue_family.into_boxed_slice());
737        }
738
739        let vk_ext_debug_utils = Some(ext::debug_utils::Device::new(
740            &physical_device.instance,
741            &device,
742        ));
743        let vk_ext_private_data = physical_device
744            .vk_ext_private_data
745            .then(|| ext::private_data::Device::new(&physical_device.instance, &device));
746        let vk_ext_private_data_slot = vk_ext_private_data
747            .as_ref()
748            .map(|vk_ext_private_data| unsafe {
749                vk_ext_private_data
750                    .create_private_data_slot(
751                        &vk::PrivateDataSlotCreateInfoEXT::default()
752                            .flags(vk::PrivateDataSlotCreateFlagsEXT::empty()),
753                        None,
754                    )
755                    .map_err(|err| {
756                        warn!("unable to create private data slot: {err}");
757
758                        DriverError::Unsupported
759                    })
760            })
761            .transpose()?;
762        let vk_khr_present_wait = physical_device
763            .vk_khr_present_wait
764            .is_some()
765            .then(|| khr::present_wait::Device::new(&physical_device.instance, &device));
766        let vk_khr_surface = physical_device.vk_khr_swapchain.then(|| {
767            let entry = Instance::entry(&physical_device.instance);
768            khr::surface::Instance::new(entry, &physical_device.instance)
769        });
770        let vk_khr_swapchain = physical_device
771            .vk_khr_swapchain
772            .then(|| khr::swapchain::Device::new(&physical_device.instance, &device));
773        let vk_khr_acceleration_structure = physical_device
774            .vk_khr_acceleration_structure
775            .is_some()
776            .then(|| khr::acceleration_structure::Device::new(&physical_device.instance, &device));
777        let vk_khr_ray_tracing_pipeline = physical_device
778            .vk_khr_ray_tracing_pipeline
779            .as_ref()
780            .is_some_and(|ext| ext.features.ray_tracing_pipeline)
781            .then(|| khr::ray_tracing_pipeline::Device::new(&physical_device.instance, &device));
782        let vk_khr_synchronization2 = physical_device
783            .vk_khr_synchronization2
784            .then(|| khr::synchronization2::Device::new(&physical_device.instance, &device));
785
786        let pipeline_cache =
787            unsafe { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None) }
788                .map_err(|err| {
789                    warn!("unable to create pipeline cache: {err}");
790
791                    DriverError::Unsupported
792                })?;
793
794        Ok(Self {
795            read_only: ReadOnlyDevice {
796                inner: Arc::new(DeviceInner {
797                    allocator: ManuallyDrop::new(Mutex::new(allocator)),
798                    device,
799                    pipeline_cache,
800                    queues: queues.into_boxed_slice(),
801                    vk_ext_debug_utils,
802                    vk_ext_private_data,
803                    vk_khr_acceleration_structure,
804                    vk_khr_present_wait,
805                    vk_khr_ray_tracing_pipeline,
806                    vk_khr_surface,
807                    vk_khr_swapchain,
808                    vk_khr_synchronization2,
809                    private_data_slot: vk_ext_private_data_slot,
810                    private_data_name_id: AtomicU64::new(0),
811                    private_data_metadata: Mutex::new(Default::default()),
812                }),
813                physical: Box::new(physical_device),
814            },
815        })
816    }
817
818    /// Constructs a new device using the given configuration.
819    #[profiling::function]
820    pub fn try_from_display(
821        display: impl HasDisplayHandle,
822        info: impl Into<DeviceInfo>,
823    ) -> Result<Self, DriverError> {
824        let DeviceInfo {
825            debug,
826            physical_device_index,
827        } = info.into();
828        let instance_info = InstanceInfoBuilder::default().debug(debug);
829        let instance = Instance::try_from_display(display, instance_info)?;
830        let physical_device = select_physical_device(&instance, physical_device_index)?;
831
832        Self::try_from_physical_device(physical_device)
833    }
834
835    /// Constructs a new device using the given physical device.
836    #[profiling::function]
837    pub fn try_from_physical_device(physical_device: PhysicalDevice) -> Result<Self, DriverError> {
838        let device = unsafe {
839            physical_device.create_ash_device(|device_create_info| {
840                physical_device.instance.create_device(
841                    physical_device.handle,
842                    &device_create_info,
843                    None,
844                )
845            })
846        }
847        .map_err(|err| {
848            error!("unable to create device: {err}");
849
850            DriverError::Unsupported
851        })?;
852
853        info!("created {}", physical_device.properties_v1_0.device_name);
854
855        unsafe { Self::try_from_ash(device, physical_device) }
856    }
857
858    pub(crate) fn try_clear_private_data_object_name<T>(
859        this: &Self,
860        object_type: vk::ObjectType,
861        object_handle: T,
862    ) where
863        T: vk::Handle + Copy,
864    {
865        let _ = Self::clear_private_data_object_name(this, object_type, object_handle);
866    }
867
868    /// Assigns a Vulkan debug-utils name to `handle` when debug labeling is enabled.
869    ///
870    /// Returns without doing any work if debug mode is `false`.
871    pub fn try_set_debug_utils_object_name<T>(
872        this: &Self,
873        object_handle: T,
874        object_name: impl AsRef<str>,
875    ) where
876        T: vk::Handle + Copy,
877    {
878        let _ = Self::set_debug_utils_object_name(this, object_handle, object_name);
879    }
880
881    /// Stores a Vulkan name in private-data metadata.
882    ///
883    /// Returns without doing any work when `VK_EXT_private_data` is not available.
884    pub(crate) fn try_set_private_data_object_name<T>(
885        this: &Self,
886        object_type: vk::ObjectType,
887        object_handle: T,
888        object_name: impl AsRef<str>,
889    ) where
890        T: vk::Handle + Copy,
891    {
892        let _ = Self::set_private_data_object_name(this, object_type, object_handle, object_name);
893    }
894
895    fn try_vk_ext_debug_utils(this: &Self) -> Result<&ext::debug_utils::Device, DriverError> {
896        this.inner
897            .vk_ext_debug_utils
898            .as_ref()
899            .ok_or(DriverError::Unsupported)
900    }
901
902    fn try_vk_ext_private_data(this: &Self) -> Result<&ext::private_data::Device, DriverError> {
903        this.inner
904            .vk_ext_private_data
905            .as_ref()
906            .ok_or(DriverError::Unsupported)
907    }
908
909    /// Waits for a single fence to signal.
910    #[profiling::function]
911    pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> {
912        Device::wait_for_fences(this, slice::from_ref(fence))
913    }
914
915    /// Waits for all fences in `fences` to signal.
916    ///
917    /// This first performs a short poll so uncontended waits return quickly, then falls back to an
918    /// indefinite wait while logging unusually slow completions.
919    #[profiling::function]
920    pub(crate) fn wait_for_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
921        unsafe {
922            match this.wait_for_fences(fences, true, 100) {
923                Ok(_) => return Ok(()),
924                Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
925                    error!("invalid device state: lost");
926
927                    return Err(DriverError::InvalidData);
928                }
929                Err(err) if err == vk::Result::TIMEOUT => {
930                    trace!("waiting...");
931                }
932                Err(err) => {
933                    warn!("unable to wait for fences during polling phase: {err}");
934
935                    return Err(DriverError::OutOfMemory);
936                }
937            }
938
939            let started = cfg!(debug_assertions).then(Instant::now);
940
941            match this.wait_for_fences(fences, true, u64::MAX) {
942                Ok(_) => (),
943                Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
944                    error!("invalid device state: lost");
945
946                    return Err(DriverError::InvalidData);
947                }
948                Err(err) => {
949                    warn!("unable to wait for fences to completion: {err}");
950
951                    return Err(DriverError::OutOfMemory);
952                }
953            }
954
955            if let Some(started) = started {
956                let elapsed = Instant::now() - started;
957                let elapsed_millis = elapsed.as_millis();
958
959                if elapsed_millis > 0 {
960                    warn!("slow fence wait: {} ms", elapsed_millis);
961                }
962            }
963        }
964
965        Ok(())
966    }
967
968    /// Waits for device idle.
969    pub fn wait_idle(this: &Self) -> Result<(), DriverError> {
970        unsafe {
971            this.device_wait_idle().map_err(|err| {
972                warn!("unable to wait for device idle: {err}");
973
974                match err {
975                    vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
976                    | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
977                    vk::Result::ERROR_DEVICE_LOST | vk::Result::ERROR_VALIDATION_FAILED_EXT => {
978                        DriverError::InvalidData
979                    }
980                    _ => DriverError::Unsupported,
981                }
982            })
983        }
984    }
985
986    /// Provides mutable access to the device allocator under its internal lock.
987    pub(crate) fn with_allocator<R>(this: &Self, f: impl FnOnce(&mut Allocator) -> R) -> R {
988        let allocator = this.inner.allocator.lock();
989
990        #[cfg(not(feature = "parking_lot"))]
991        let allocator = allocator.expect("poisoned allocator lock");
992
993        let mut allocator = allocator;
994
995        f(&mut allocator)
996    }
997
998    fn with_object_metadata_ids<R>(
999        this: &Self,
1000        f: impl FnOnce(&mut HashMap<(vk::ObjectType, u64), u64>) -> R,
1001    ) -> R {
1002        Self::with_private_data_metadata(this, |metadata| f(&mut metadata.object_metadata_ids))
1003    }
1004
1005    fn with_private_data_metadata<R>(
1006        this: &Self,
1007        f: impl FnOnce(&mut PrivateDataMetadata) -> R,
1008    ) -> R {
1009        let mut metadata = this.inner.private_data_metadata.lock();
1010
1011        #[cfg(not(feature = "parking_lot"))]
1012        let mut metadata = metadata.expect("poisoned private data metadata");
1013
1014        f(&mut metadata)
1015    }
1016
1017    /// Provides locked access to a device queue.
1018    ///
1019    /// Acquires the mutex for the queue at the given family and index, calls `f` with the
1020    /// [`vk::Queue`], and releases the mutex after `f` returns.
1021    ///
1022    /// # Panics
1023    ///
1024    /// Panics if `queue_family_index` or `queue_index` is out of range for this device.
1025    pub fn with_queue<R>(
1026        this: &Self,
1027        queue_family_index: u32,
1028        queue_index: u32,
1029        f: impl FnOnce(vk::Queue) -> R,
1030    ) -> R {
1031        let queue_family = this
1032            .inner
1033            .queues
1034            .get(queue_family_index as usize)
1035            .expect("invalid queue family index");
1036        let queue = queue_family
1037            .get(queue_index as usize)
1038            .expect("invalid queue index");
1039        #[cfg(not(feature = "parking_lot"))]
1040        let guard = queue.lock().expect("poisoned queue lock");
1041
1042        #[cfg(feature = "parking_lot")]
1043        let guard = queue.lock();
1044
1045        f(*guard)
1046    }
1047}
1048
1049impl Debug for Device {
1050    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
1051        f.debug_struct(stringify!(Device))
1052            .field("handle", &self.inner.device.handle())
1053            .field("physical", &self.physical)
1054            .finish_non_exhaustive()
1055    }
1056}
1057
1058#[cfg(doc)]
1059impl Deref for Device {
1060    type Target = ash::Device;
1061
1062    fn deref(&self) -> &Self::Target {
1063        unreachable!()
1064    }
1065}
1066
1067impl Eq for Device {}
1068
1069impl PartialEq for Device {
1070    fn eq(&self, other: &Self) -> bool {
1071        Arc::ptr_eq(&self.inner, &other.inner)
1072    }
1073}
1074
1075/// Information used to create a [`Device`] instance.
1076#[derive(Builder, Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
1077#[builder(
1078    build_fn(private, name = "fallible_build"),
1079    derive(Clone, Copy, Debug),
1080    pattern = "owned"
1081)]
1082pub struct DeviceInfo {
1083    /// Enables the Vulkan validation layers.
1084    ///
1085    /// This requires a Vulkan SDK installation and will panic when validation errors happen. See
1086    /// the LunarG [Vulkan Validation Layers] documentation for setup and behavior details.
1087    ///
1088    /// When `stderr` is attached to an interactive terminal, validation errors will park the
1089    /// callback thread for debugger attach.
1090    ///
1091    /// _NOTE:_ Consider turning OFF debug if you discover an unknown issue. Often the validation
1092    /// layers will report an error before other layers can provide additional context such as the
1093    /// API dump info or other messages. You might find the "actual" issue is detailed in those
1094    /// subsequent details.
1095    ///
1096    /// ## Platform-specific
1097    ///
1098    /// **macOS:** Has no effect unless the `loaded` feature is enabled.
1099    ///
1100    /// [Vulkan Validation Layers]: https://vulkan.lunarg.com/doc/sdk/latest/windows/validation_layers.html
1101    #[builder(default)]
1102    pub debug: bool,
1103
1104    /// Index of the [`PhysicalDevice`] from the available devices. See
1105    /// [`Instance::physical_devices`].
1106    #[builder(default)]
1107    pub physical_device_index: usize,
1108}
1109
1110impl DeviceInfo {
1111    /// Creates a default `DeviceInfoBuilder`.
1112    pub fn builder() -> DeviceInfoBuilder {
1113        Default::default()
1114    }
1115
1116    /// Converts a `DeviceInfo` into a `DeviceInfoBuilder`.
1117    pub fn into_builder(self) -> DeviceInfoBuilder {
1118        DeviceInfoBuilder {
1119            debug: Some(self.debug),
1120            physical_device_index: Some(self.physical_device_index),
1121        }
1122    }
1123}
1124
1125impl From<DeviceInfoBuilder> for DeviceInfo {
1126    fn from(info: DeviceInfoBuilder) -> Self {
1127        info.build()
1128    }
1129}
1130
1131impl DeviceInfoBuilder {
1132    /// Builds a new `DeviceInfo`.
1133    #[inline(always)]
1134    pub fn build(self) -> DeviceInfo {
1135        self.fallible_build().expect("invalid device info")
1136    }
1137}
1138
1139struct DeviceInner {
1140    allocator: ManuallyDrop<Mutex<Allocator>>,
1141    device: ash::Device,
1142    pipeline_cache: vk::PipelineCache,
1143    queues: Box<[Box<[Mutex<vk::Queue>]>]>,
1144    vk_ext_debug_utils: Option<ext::debug_utils::Device>,
1145    vk_ext_private_data: Option<ext::private_data::Device>,
1146    vk_khr_acceleration_structure: Option<khr::acceleration_structure::Device>,
1147    vk_khr_present_wait: Option<khr::present_wait::Device>,
1148    vk_khr_ray_tracing_pipeline: Option<khr::ray_tracing_pipeline::Device>,
1149    vk_khr_surface: Option<khr::surface::Instance>,
1150    vk_khr_swapchain: Option<khr::swapchain::Device>,
1151    vk_khr_synchronization2: Option<khr::synchronization2::Device>,
1152    private_data_slot: Option<vk::PrivateDataSlot>,
1153    private_data_name_id: AtomicU64,
1154    private_data_metadata: Mutex<PrivateDataMetadata>,
1155}
1156
1157#[derive(Default)]
1158struct PrivateDataMetadata {
1159    object_metadata_ids: HashMap<(vk::ObjectType, u64), u64>,
1160    names: HashMap<u64, String>,
1161}
1162
1163impl Drop for DeviceInner {
1164    #[profiling::function]
1165    fn drop(&mut self) {
1166        if panicking() {
1167            // When panicking we don't want the GPU allocator to complain about leaks
1168            unsafe {
1169                forget(ManuallyDrop::take(&mut self.allocator));
1170            }
1171
1172            return;
1173        }
1174
1175        // trace!("drop");
1176
1177        if let Err(err) = unsafe { self.device.device_wait_idle() } {
1178            warn!("device_wait_idle() failed: {err}");
1179        }
1180
1181        unsafe {
1182            self.device
1183                .destroy_pipeline_cache(self.pipeline_cache, None);
1184
1185            if let (Some(vk_ext_private_data), Some(private_data_slot)) = (
1186                self.vk_ext_private_data.as_ref(),
1187                self.private_data_slot.take(),
1188            ) {
1189                vk_ext_private_data.destroy_private_data_slot(private_data_slot, None);
1190            }
1191
1192            ManuallyDrop::drop(&mut self.allocator);
1193        }
1194
1195        unsafe {
1196            self.device.destroy_device(None);
1197        }
1198    }
1199}
1200
1201#[doc(hidden)]
1202impl Clone for ReadOnlyDevice {
1203    fn clone(&self) -> Self {
1204        Self {
1205            inner: self.inner.clone(),
1206            physical: self.physical.clone(),
1207        }
1208    }
1209}
1210
1211#[doc(hidden)]
1212impl Deref for ReadOnlyDevice {
1213    type Target = ash::Device;
1214
1215    fn deref(&self) -> &Self::Target {
1216        &self.inner.device
1217    }
1218}
1219
1220#[cfg(test)]
1221mod test {
1222    use super::*;
1223
1224    type Info = DeviceInfo;
1225    type Builder = DeviceInfoBuilder;
1226
1227    #[test]
1228    pub fn device_info() {
1229        Info::default().into_builder().build();
1230    }
1231
1232    #[test]
1233    pub fn device_info_builder() {
1234        Builder::default().build();
1235    }
1236}