1use {
4 super::{
5 DriverError,
6 instance::{Instance, InstanceInfoBuilder},
7 physical_device::{PhysicalDevice, RayTraceProperties},
8 },
9 ash::{khr, vk},
10 derive_builder::{Builder, UninitializedFieldError},
11 gpu_allocator::{
12 AllocatorDebugSettings,
13 vulkan::{Allocator, AllocatorCreateDesc},
14 },
15 log::{error, info, trace, warn},
16 raw_window_handle::HasDisplayHandle,
17 std::{
18 fmt::{Debug, Formatter},
19 mem::{ManuallyDrop, forget},
20 ops::Deref,
21 slice,
22 sync::Arc,
23 thread::panicking,
24 time::Instant,
25 },
26};
27
28#[cfg(feature = "parking_lot")]
29use parking_lot::Mutex;
30
31#[cfg(not(feature = "parking_lot"))]
32use std::sync::Mutex;
33
34fn select_physical_device(
35 instance: &Instance,
36 mut index: usize,
37) -> Result<PhysicalDevice, DriverError> {
38 let mut physical_devices = Instance::physical_devices(instance)?
39 .into_iter()
40 .collect::<Vec<_>>();
41 if physical_devices.is_empty() {
42 warn!("unable to find physical devices");
43
44 return Err(DriverError::Unsupported);
45 }
46
47 if index >= physical_devices.len() {
48 index = 0;
49 }
50
51 let physical_device = physical_devices.remove(index);
52
53 Ok(physical_device)
54}
55
56#[read_only::embed]
58#[derive(Clone)]
59pub struct Device {
60 #[readonly]
61 pub(self) inner: Arc<DeviceInner>,
62
63 #[readonly]
67 pub physical_device: Box<PhysicalDevice>,
68}
69
70impl Device {
71 #[deprecated = "use create"]
72 #[doc(hidden)]
73 pub fn new(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
74 Self::create(info)
75 }
76
77 pub fn begin_command_buffer(
82 this: &Self,
83 cmd: vk::CommandBuffer,
84 begin_info: &vk::CommandBufferBeginInfo,
85 ) -> Result<(), DriverError> {
86 unsafe {
87 this.begin_command_buffer(cmd, begin_info).map_err(|err| {
88 warn!("unable to begin command buffer: {err}");
89
90 match err {
91 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
92 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
93 _ => DriverError::Unsupported,
94 }
95 })
96 }
97 }
98
99 #[profiling::function]
105 pub fn create(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
106 let DeviceInfo {
107 debug,
108 physical_device_index,
109 } = info.into();
110 let instance_info = InstanceInfoBuilder::default().debug(debug);
111 let instance = Instance::create(instance_info)?;
112 let physical_device = select_physical_device(&instance, physical_device_index)?;
113
114 Self::try_from_physical_device(physical_device)
115 }
116
117 pub(crate) fn create_fence(this: &Self, signaled: bool) -> Result<vk::Fence, DriverError> {
118 let mut flags = vk::FenceCreateFlags::empty();
119
120 if signaled {
121 flags |= vk::FenceCreateFlags::SIGNALED;
122 }
123
124 let create_info = vk::FenceCreateInfo::default().flags(flags);
125 let allocation_callbacks = None;
126
127 unsafe { this.create_fence(&create_info, allocation_callbacks) }.map_err(|err| {
128 warn!("unable to create fence: {err}");
129
130 DriverError::OutOfMemory
131 })
132 }
133
134 pub fn end_command_buffer(this: &Self, cmd: vk::CommandBuffer) -> Result<(), DriverError> {
139 unsafe {
140 this.end_command_buffer(cmd).map_err(|err| {
141 warn!("unable to end command buffer: {err}");
142
143 match err {
144 vk::Result::ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR => DriverError::InvalidData,
145 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
146 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
147 _ => DriverError::Unsupported,
148 }
149 })
150 }
151 }
152
153 pub(crate) fn expect_accel_struct_ext(this: &Self) -> &khr::acceleration_structure::Device {
160 this.inner
161 .accel_struct_ext
162 .as_ref()
163 .expect("missing VK_KHR_acceleration_structure")
164 }
165
166 pub(crate) fn expect_ray_trace_ext(this: &Self) -> &khr::ray_tracing_pipeline::Device {
173 this.inner
174 .ray_trace_ext
175 .as_ref()
176 .expect("missing VK_KHR_ray_tracing_pipeline")
177 }
178
179 pub(crate) fn expect_ray_trace_properties(this: &Self) -> &RayTraceProperties {
186 this.physical_device
187 .ray_trace_properties
188 .as_ref()
189 .expect("missing VK_KHR_ray_tracing_pipeline")
190 }
191
192 pub(crate) fn expect_surface_ext(this: &Self) -> &khr::surface::Instance {
198 this.inner
199 .surface_ext
200 .as_ref()
201 .expect("missing VK_KHR_surface")
202 }
203
204 pub(crate) fn expect_swapchain_ext(this: &Self) -> &khr::swapchain::Device {
210 this.inner
211 .swapchain_ext
212 .as_ref()
213 .expect("missing VK_KHR_swapchain")
214 }
215
216 pub(crate) fn pipeline_cache(this: &Self) -> vk::PipelineCache {
217 this.inner.pipeline_cache
218 }
219
220 pub fn queue_submit(
222 this: &Self,
223 queue: vk::Queue,
224 submits: &[vk::SubmitInfo],
225 fence: vk::Fence,
226 ) -> Result<(), DriverError> {
227 unsafe {
228 this.queue_submit(queue, submits, fence).map_err(|err| {
229 warn!("unable to queue submits: {err}");
230
231 match err {
232 vk::Result::ERROR_DEVICE_LOST => DriverError::InvalidData,
233 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY
234 | vk::Result::ERROR_OUT_OF_HOST_MEMORY => DriverError::OutOfMemory,
235 _ => DriverError::Unsupported,
236 }
237 })
238 }
239 }
240
241 pub fn reset_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
243 unsafe {
244 this.reset_fences(fences).map_err(|err| {
245 warn!("unable to reset fences: {err}");
246
247 match err {
248 vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => DriverError::OutOfMemory,
249 _ => DriverError::Unsupported,
250 }
251 })
252 }
253 }
254
255 #[profiling::function]
257 pub fn try_from_ash_device(
258 device: ash::Device,
259 physical_device: PhysicalDevice,
260 ) -> Result<Self, DriverError> {
261 let debug = physical_device.instance.info.debug;
262 let mut debug_settings = AllocatorDebugSettings::default();
263 debug_settings.log_leaks_on_shutdown = debug;
264 debug_settings.log_memory_information = debug;
265 debug_settings.log_allocations = debug;
266
267 let allocator = Allocator::new(&AllocatorCreateDesc {
268 instance: (*physical_device.instance).clone(),
269 device: device.clone(),
270 physical_device: physical_device.handle,
271 debug_settings,
272 buffer_device_address: true,
273 allocation_sizes: Default::default(),
274 })
275 .map_err(|err| {
276 warn!("unable to create allocator: {err}");
277
278 DriverError::Unsupported
279 })?;
280
281 let mut queues = Vec::with_capacity(physical_device.queue_families.len());
282
283 for (queue_family_index, properties) in physical_device.queue_families.iter().enumerate() {
284 let mut queue_family = Vec::with_capacity(properties.queue_count as _);
285
286 for queue_index in 0..properties.queue_count {
287 queue_family.push(Mutex::new(unsafe {
288 device.get_device_queue(queue_family_index as _, queue_index)
289 }));
290 }
291
292 queues.push(queue_family.into_boxed_slice());
293 }
294
295 let surface_ext = physical_device.swapchain_ext.then(|| {
296 let entry = Instance::entry(&physical_device.instance);
297 khr::surface::Instance::new(entry, &physical_device.instance)
298 });
299 let swapchain_ext = physical_device
300 .swapchain_ext
301 .then(|| khr::swapchain::Device::new(&physical_device.instance, &device));
302 let accel_struct_ext = physical_device
303 .accel_struct_properties
304 .is_some()
305 .then(|| khr::acceleration_structure::Device::new(&physical_device.instance, &device));
306 let ray_trace_ext = physical_device
307 .ray_trace_features
308 .ray_tracing_pipeline
309 .then(|| khr::ray_tracing_pipeline::Device::new(&physical_device.instance, &device));
310
311 let pipeline_cache =
312 unsafe { device.create_pipeline_cache(&vk::PipelineCacheCreateInfo::default(), None) }
313 .map_err(|err| {
314 warn!("unable to create pipeline cache: {err}");
315
316 DriverError::Unsupported
317 })?;
318
319 Ok(Self {
320 read_only: ReadOnlyDevice {
321 inner: Arc::new(DeviceInner {
322 accel_struct_ext,
323 allocator: ManuallyDrop::new(Mutex::new(allocator)),
324 device,
325 pipeline_cache,
326 queues: queues.into_boxed_slice(),
327 ray_trace_ext,
328 surface_ext,
329 swapchain_ext,
330 }),
331 physical_device: Box::new(physical_device),
332 },
333 })
334 }
335
336 #[profiling::function]
338 pub fn try_from_display(
339 display: impl HasDisplayHandle,
340 info: impl Into<DeviceInfo>,
341 ) -> Result<Self, DriverError> {
342 let DeviceInfo {
343 debug,
344 physical_device_index,
345 } = info.into();
346 let instance_info = InstanceInfoBuilder::default().debug(debug);
347 let instance = Instance::try_from_display(display, instance_info)?;
348 let physical_device = select_physical_device(&instance, physical_device_index)?;
349
350 Self::try_from_physical_device(physical_device)
351 }
352
353 #[profiling::function]
355 pub fn try_from_physical_device(physical_device: PhysicalDevice) -> Result<Self, DriverError> {
356 let device = unsafe {
357 physical_device.create_ash_device(|device_create_info| {
358 physical_device.instance.create_device(
359 physical_device.handle,
360 &device_create_info,
361 None,
362 )
363 })
364 }
365 .map_err(|err| {
366 error!("unable to create device: {err}");
367
368 DriverError::Unsupported
369 })?;
370
371 info!("created {}", physical_device.properties_v1_0.device_name);
372
373 Self::try_from_ash_device(device, physical_device)
374 }
375
376 #[profiling::function]
377 pub(crate) fn wait_for_fence(this: &Self, fence: &vk::Fence) -> Result<(), DriverError> {
378 Device::wait_for_fences(this, slice::from_ref(fence))
379 }
380
381 #[profiling::function]
382 pub(crate) fn wait_for_fences(this: &Self, fences: &[vk::Fence]) -> Result<(), DriverError> {
383 unsafe {
384 match this.wait_for_fences(fences, true, 100) {
385 Ok(_) => return Ok(()),
386 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
387 error!("invalid device state: lost");
388
389 return Err(DriverError::InvalidData);
390 }
391 Err(err) if err == vk::Result::TIMEOUT => {
392 trace!("waiting...");
393 }
394 Err(err) => {
395 warn!("unable to wait for fences during polling phase: {err}");
396
397 return Err(DriverError::OutOfMemory);
398 }
399 }
400
401 let started = Instant::now();
402
403 match this.wait_for_fences(fences, true, u64::MAX) {
404 Ok(_) => (),
405 Err(err) if err == vk::Result::ERROR_DEVICE_LOST => {
406 error!("invalid device state: lost");
407
408 return Err(DriverError::InvalidData);
409 }
410 Err(err) => {
411 warn!("unable to wait for fences to completion: {err}");
412
413 return Err(DriverError::OutOfMemory);
414 }
415 }
416
417 let elapsed = Instant::now() - started;
418 let elapsed_millis = elapsed.as_millis();
419
420 if elapsed_millis > 0 {
421 warn!("slow fence wait: {} ms", elapsed_millis);
422 }
423 }
424
425 Ok(())
426 }
427
428 pub(crate) fn with_allocator<R>(this: &Self, f: impl FnOnce(&mut Allocator) -> R) -> R {
429 let allocator = this.inner.allocator.lock();
430
431 #[cfg(not(feature = "parking_lot"))]
432 let allocator = allocator.expect("poisoned allocator lock");
433
434 let mut allocator = allocator;
435
436 f(&mut allocator)
437 }
438
439 pub fn with_queue<R>(
448 this: &Self,
449 queue_family_index: u32,
450 queue_index: u32,
451 f: impl FnOnce(vk::Queue) -> R,
452 ) -> R {
453 let queue_family = this
454 .inner
455 .queues
456 .get(queue_family_index as usize)
457 .expect("invalid queue family index");
458 let queue = queue_family
459 .get(queue_index as usize)
460 .expect("invalid queue index");
461 #[cfg(not(feature = "parking_lot"))]
462 let guard = queue.lock().expect("poisoned queue lock");
463
464 #[cfg(feature = "parking_lot")]
465 let guard = queue.lock();
466
467 f(*guard)
468 }
469}
470
471impl Debug for Device {
472 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
473 f.write_str("Device")
474 }
475}
476
477#[cfg(doc)]
478impl Deref for Device {
479 type Target = ash::Device;
480
481 fn deref(&self) -> &Self::Target {
482 unreachable!()
483 }
484}
485
486impl Eq for Device {}
487
488impl PartialEq for Device {
489 fn eq(&self, other: &Self) -> bool {
490 Arc::ptr_eq(&self.inner, &other.inner)
491 }
492}
493
494#[derive(Builder, Clone, Copy, Debug, Default, Eq, PartialEq, Hash)]
496#[builder(
497 build_fn(private, name = "fallible_build", error = "UninitializedFieldError"),
498 derive(Clone, Copy, Debug),
499 pattern = "owned"
500)]
501pub struct DeviceInfo {
502 #[builder(default)]
516 pub debug: bool,
517
518 #[builder(default)]
521 pub physical_device_index: usize,
522}
523
524impl DeviceInfo {
525 pub fn builder() -> DeviceInfoBuilder {
527 Default::default()
528 }
529
530 pub fn into_builder(self) -> DeviceInfoBuilder {
532 DeviceInfoBuilder {
533 debug: Some(self.debug),
534 physical_device_index: Some(self.physical_device_index),
535 }
536 }
537
538 #[deprecated = "use into_builder function"]
539 #[doc(hidden)]
540 pub fn to_builder(self) -> DeviceInfoBuilder {
541 self.into_builder()
542 }
543}
544
545impl From<DeviceInfoBuilder> for DeviceInfo {
546 fn from(info: DeviceInfoBuilder) -> Self {
547 info.build()
548 }
549}
550
551impl DeviceInfoBuilder {
552 #[inline(always)]
554 pub fn build(self) -> DeviceInfo {
555 self.fallible_build().expect("invalid device info")
556 }
557}
558
559struct DeviceInner {
560 accel_struct_ext: Option<khr::acceleration_structure::Device>,
561 allocator: ManuallyDrop<Mutex<Allocator>>,
562 device: ash::Device,
563 pipeline_cache: vk::PipelineCache,
564 queues: Box<[Box<[Mutex<vk::Queue>]>]>,
565 ray_trace_ext: Option<khr::ray_tracing_pipeline::Device>,
566 surface_ext: Option<khr::surface::Instance>,
567 swapchain_ext: Option<khr::swapchain::Device>,
568}
569
570impl Drop for DeviceInner {
571 #[profiling::function]
572 fn drop(&mut self) {
573 if panicking() {
574 unsafe {
576 forget(ManuallyDrop::take(&mut self.allocator));
577 }
578
579 return;
580 }
581
582 if let Err(err) = unsafe { self.device.device_wait_idle() } {
585 warn!("device_wait_idle() failed: {err}");
586 }
587
588 unsafe {
589 self.device
590 .destroy_pipeline_cache(self.pipeline_cache, None);
591
592 ManuallyDrop::drop(&mut self.allocator);
593 }
594
595 unsafe {
596 self.device.destroy_device(None);
597 }
598 }
599}
600
601#[doc(hidden)]
602impl Clone for ReadOnlyDevice {
603 fn clone(&self) -> Self {
604 Self {
605 inner: self.inner.clone(),
606 physical_device: self.physical_device.clone(),
607 }
608 }
609}
610
611#[doc(hidden)]
612impl Deref for ReadOnlyDevice {
613 type Target = ash::Device;
614
615 fn deref(&self) -> &Self::Target {
616 &self.inner.device
617 }
618}
619
620#[allow(deprecated)]
621#[allow(unused)]
622pub(crate) mod deprecated {
623 use {
624 crate::driver::{
625 DriverError,
626 device::{Device, DeviceInfo, DeviceInfoBuilder},
627 },
628 ash::vk,
629 log::warn,
630 raw_window_handle::HasDisplayHandle,
631 std::any::Any,
632 };
633
634 impl Device {
635 #[deprecated = "use from_display function"]
636 #[doc(hidden)]
637 pub fn create_display(
638 info: impl Into<DeviceInfo>,
639 display_handle: &impl HasDisplayHandle,
640 ) -> Result<Self, DriverError> {
641 Self::try_from_display(display_handle, info)
642 }
643
644 #[deprecated = "use new function"]
645 #[doc(hidden)]
646 pub fn create_headless(info: impl Into<DeviceInfo>) -> Result<Self, DriverError> {
647 Self::new(info)
648 }
649 #[deprecated = "use format_properties function of physical_device field"]
650 #[doc(hidden)]
651 pub fn format_properties(this: &Self, format: vk::Format) -> vk::FormatProperties {
652 this.physical_device.format_properties(format)
653 }
654
655 #[deprecated = "use image_format_properties function of physical_device field"]
656 #[doc(hidden)]
657 pub fn image_format_properties(
658 this: &Self,
659 format: vk::Format,
660 ty: vk::ImageType,
661 tiling: vk::ImageTiling,
662 usage: vk::ImageUsageFlags,
663 flags: vk::ImageCreateFlags,
664 ) -> Result<Option<vk::ImageFormatProperties>, DriverError> {
665 this.physical_device
666 .image_format_properties(format, ty, tiling, usage, flags)
667 }
668 }
669
670 impl DeviceInfo {
671 #[deprecated = "no effect; use physical_device_index"]
672 #[doc(hidden)]
673 pub fn integrated_gpu() {
674 warn!("invalid deprecated device selection hint: integrated_gpu has no effect");
675 }
676
677 #[deprecated = "no effect; use physical_device_index"]
678 #[doc(hidden)]
679 pub fn discrete_gpu() {
680 warn!("invalid deprecated device selection hint: discrete_gpu has no effect");
681 }
682 }
683
684 impl DeviceInfoBuilder {
685 #[deprecated = "no effect; use physical_device_index"]
686 #[doc(hidden)]
687 pub fn select_physical_device(self, _: Box<dyn Fn()>) -> Self {
688 warn!(
689 "invalid deprecated device selection callback: select_physical_device has no effect"
690 );
691
692 self
693 }
694 }
695}
696
697#[cfg(test)]
698mod test {
699 use super::*;
700
701 type Info = DeviceInfo;
702 type Builder = DeviceInfoBuilder;
703
704 #[test]
705 pub fn device_info() {
706 Info::default().into_builder().build();
707 }
708
709 #[test]
710 pub fn device_info_builder() {
711 Builder::default().build();
712 }
713}