bort_vk/
device.rs

1use crate::{
2    ApiVersion, DebugCallback, Fence, Instance, PhysicalDevice, Queue, ALLOCATION_CALLBACK_NONE,
3};
4use ash::{
5    prelude::VkResult,
6    vk::{self, DeviceQueueCreateInfo, ExtendsDeviceCreateInfo},
7};
8use std::{
9    error,
10    ffi::{CString, NulError},
11    fmt,
12    os::raw::c_char,
13    sync::Arc,
14};
15
16pub trait DeviceOwned {
17    fn device(&self) -> &Arc<Device>;
18    fn handle_raw(&self) -> u64;
19}
20
21pub struct Device {
22    inner: ash::Device,
23    debug_callback_ref: Option<Arc<DebugCallback>>,
24
25    // dependencies
26    physical_device: Arc<PhysicalDevice>,
27}
28
29impl Device {
30    /// `features_1_1`, `features_1_2` and `features_1_3` might get ignored depending on the
31    /// `instance` api version.
32    pub fn new<'a>(
33        physical_device: Arc<PhysicalDevice>,
34        queue_create_infos: impl IntoIterator<Item = vk::DeviceQueueCreateInfoBuilder<'a>>,
35        features_1_0: vk::PhysicalDeviceFeatures,
36        features_1_1: vk::PhysicalDeviceVulkan11Features,
37        features_1_2: vk::PhysicalDeviceVulkan12Features,
38        features_1_3: vk::PhysicalDeviceVulkan13Features,
39        extension_names: Vec<CString>,
40        layer_names: Vec<CString>,
41        debug_callback_ref: Option<Arc<DebugCallback>>,
42    ) -> Result<Self, DeviceError> {
43        let queue_create_infos_built: Vec<DeviceQueueCreateInfo> = queue_create_infos
44            .into_iter()
45            .map(move |create_info| create_info.build())
46            .collect();
47        unsafe {
48            Self::new_with_p_next_chain(
49                physical_device,
50                &queue_create_infos_built,
51                features_1_0,
52                features_1_1,
53                features_1_2,
54                features_1_3,
55                extension_names,
56                layer_names,
57                debug_callback_ref,
58                Vec::<vk::PhysicalDeviceFeatures2>::new(),
59            )
60        }
61    }
62
63    /// `features_1_1`, `features_1_2` and `features_1_3` might get ignored depending on the
64    /// `instance` api version.
65    ///
66    /// _Note that each member of `p_next_structs` can only be one type (known at compile time)
67    /// because the ash fn `push_next` currently requires the template to be `Sized`. Just treat
68    /// it like an `Option` (either 0 or 1 element) and create your own p_next chain in the one
69    /// element you pass to this until the next version of ash is released._
70    ///
71    /// Safety:
72    /// No busted pointers in the last element of `p_next_structs`.
73    pub unsafe fn new_with_p_next_chain<'a>(
74        physical_device: Arc<PhysicalDevice>,
75        queue_create_infos: &'a [vk::DeviceQueueCreateInfo],
76        features_1_0: vk::PhysicalDeviceFeatures,
77        mut features_1_1: vk::PhysicalDeviceVulkan11Features,
78        mut features_1_2: vk::PhysicalDeviceVulkan12Features,
79        mut features_1_3: vk::PhysicalDeviceVulkan13Features,
80        extension_names: Vec<CString>,
81        layer_names: Vec<CString>,
82        debug_callback_ref: Option<Arc<DebugCallback>>,
83        mut p_next_structs: Vec<impl ExtendsDeviceCreateInfo>,
84    ) -> Result<Self, DeviceError> {
85        let instance = physical_device.instance();
86
87        let extension_name_ptrs: Vec<*const c_char> = extension_names
88            .iter()
89            .map(|cstring| cstring.as_ptr())
90            .collect();
91        let layer_name_ptrs: Vec<*const c_char> =
92            layer_names.iter().map(|cstring| cstring.as_ptr()).collect();
93
94        #[allow(deprecated)] // backward compatability
95        let mut device_create_info = vk::DeviceCreateInfo::builder()
96            .queue_create_infos(queue_create_infos)
97            .enabled_extension_names(&extension_name_ptrs)
98            .enabled_layer_names(&layer_name_ptrs);
99
100        let mut features_2 = vk::PhysicalDeviceFeatures2::builder();
101        let max_api_version = instance.max_api_version();
102
103        if max_api_version <= ApiVersion::new(1, 0) {
104            device_create_info = device_create_info.enabled_features(&features_1_0);
105        } else {
106            features_2 = features_2.features(features_1_0);
107            device_create_info = device_create_info.push_next(&mut features_2);
108
109            if max_api_version >= ApiVersion::new(1, 1) {
110                device_create_info = device_create_info.push_next(&mut features_1_1)
111            }
112            if max_api_version >= ApiVersion::new(1, 2) {
113                device_create_info = device_create_info.push_next(&mut features_1_2);
114            }
115            if max_api_version >= ApiVersion::new(1, 3) {
116                device_create_info = device_create_info.push_next(&mut features_1_3);
117            }
118        }
119
120        for p_next_struct in &mut p_next_structs {
121            device_create_info = device_create_info.push_next(p_next_struct);
122        }
123
124        Self::new_from_create_info(physical_device, device_create_info, debug_callback_ref)
125    }
126
127    /// Safety:
128    /// No busted pointers in `create_info_builder` or its referenced structs (e.g. p_next chain).
129    pub unsafe fn new_from_create_info(
130        physical_device: Arc<PhysicalDevice>,
131        create_info_builder: vk::DeviceCreateInfoBuilder,
132        debug_callback_ref: Option<Arc<DebugCallback>>,
133    ) -> Result<Self, DeviceError> {
134        let inner = unsafe {
135            physical_device.instance().inner().create_device(
136                physical_device.handle(),
137                &create_info_builder,
138                ALLOCATION_CALLBACK_NONE,
139            )
140        }
141        .map_err(|vk_res| DeviceError::Creation(vk_res))?;
142
143        Ok(Self {
144            inner,
145            debug_callback_ref,
146            physical_device,
147        })
148    }
149
150    /// Store a reference to a debug callback. The means that the debug callback won't be dropped
151    /// (and destroyed) until this device is! Handy to make sure that you still get validation
152    /// while device resources are being dropped/destroyed.
153    pub fn set_debug_callback_ref(&mut self, debug_callback_ref: Option<Arc<DebugCallback>>) {
154        self.debug_callback_ref = debug_callback_ref;
155    }
156
157    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkDeviceWaitIdle.html>
158    pub fn wait_idle(&self) -> Result<(), DeviceError> {
159        let res = unsafe { self.inner.device_wait_idle() };
160        res.map_err(|vk_res| DeviceError::WaitIdle(vk_res))
161    }
162
163    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkQueueWaitIdle.html>
164    pub fn queue_wait_idle(&self, queue: &Queue) -> Result<(), DeviceError> {
165        let res = unsafe { self.inner.queue_wait_idle(queue.handle()) };
166        res.map_err(|vk_res| DeviceError::WaitIdle(vk_res))
167    }
168
169    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkUpdateDescriptorSets.html>
170    pub fn update_descriptor_sets<'a>(
171        &self,
172        descriptor_writes: impl IntoIterator<Item = vk::WriteDescriptorSetBuilder<'a>>,
173        descriptor_copies: impl IntoIterator<Item = vk::CopyDescriptorSetBuilder<'a>>,
174    ) {
175        let descriptor_writes_built: Vec<vk::WriteDescriptorSet> = descriptor_writes
176            .into_iter()
177            .map(|descriptor_write| descriptor_write.build())
178            .collect();
179        let descriptor_copies_built: Vec<vk::CopyDescriptorSet> = descriptor_copies
180            .into_iter()
181            .map(|descriptor_copy| descriptor_copy.build())
182            .collect();
183        unsafe {
184            self.inner
185                .update_descriptor_sets(&descriptor_writes_built, &descriptor_copies_built)
186        }
187    }
188
189    /// <https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkWaitForFences.html>
190    pub fn wait_for_fences<'a>(
191        &self,
192        fences: impl IntoIterator<Item = &'a Fence>,
193        wait_all: bool,
194        timeout: u64,
195    ) -> VkResult<()> {
196        let fence_hanles: Vec<vk::Fence> = fences.into_iter().map(|fence| fence.handle()).collect();
197        unsafe { self.inner.wait_for_fences(&fence_hanles, wait_all, timeout) }
198    }
199
200    // Getters
201
202    /// Access the `ash::Device` struct that `self` contains. Allows you to access vulkan device
203    /// functions.
204    #[inline]
205    pub fn inner(&self) -> &ash::Device {
206        &self.inner
207    }
208
209    #[inline]
210    pub fn physical_device(&self) -> &Arc<PhysicalDevice> {
211        &self.physical_device
212    }
213
214    #[inline]
215    pub fn instance(&self) -> &Arc<Instance> {
216        self.physical_device.instance()
217    }
218
219    #[inline]
220    pub fn debug_callback_ref(&self) -> &Option<Arc<DebugCallback>> {
221        &self.debug_callback_ref
222    }
223}
224
225impl Drop for Device {
226    fn drop(&mut self) {
227        self.wait_idle().expect("vkDeviceWaitIdle");
228        unsafe {
229            self.inner.destroy_device(ALLOCATION_CALLBACK_NONE);
230        }
231    }
232}
233
234#[derive(Debug, Clone)]
235pub enum DeviceError {
236    ExtensionStringConversion(NulError),
237    LayerStringConversion(NulError),
238    Creation(vk::Result),
239    WaitIdle(vk::Result),
240}
241
242impl fmt::Display for DeviceError {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        match self {
245            Self::ExtensionStringConversion(e) => {
246                write!(
247                    f,
248                    "failed to convert extension name string to c string: {}",
249                    e
250                )
251            }
252            Self::LayerStringConversion(e) => {
253                write!(f, "failed to convert layer name string to c string: {}", e)
254            }
255            Self::Creation(e) => write!(f, "failed to create device: {}", e),
256            Self::WaitIdle(e) => write!(f, "vkDeviceWaitIdle call failed: {}", e),
257        }
258    }
259}
260
261impl error::Error for DeviceError {
262    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
263        match self {
264            Self::ExtensionStringConversion(e) => Some(e),
265            Self::LayerStringConversion(e) => Some(e),
266            Self::Creation(e) => Some(e),
267            Self::WaitIdle(e) => Some(e),
268        }
269    }
270}