Skip to main content

lgpu_hal/vulkan/
device.rs

1//! Vulkan logical device and execution context.
2
3use super::{allocator, bindless, VulkanCommandRecorder};
4use crate::HalError;
5use ash::vk;
6
7pub struct VulkanDevice {
8    pub device: ash::Device,
9    pub physical_device: vk::PhysicalDevice,
10    pub queue: std::sync::Mutex<vk::Queue>,
11    pub queue_family_index: u32,
12    pub memory_properties: vk::PhysicalDeviceMemoryProperties,
13    pub non_coherent_atom_size: u64,
14    pub bindless_heap: Option<bindless::VulkanBindlessHeap>,
15    pub swapchain_loader: Option<ash::khr::swapchain::Device>,
16    pub command_recorder_cache: std::sync::Mutex<Vec<(vk::CommandPool, vk::CommandBuffer)>>,
17    pub fence_cache: std::sync::Mutex<Vec<vk::Fence>>,
18    pub internal_frame_semaphore: vk::Semaphore,
19    pub internal_frame_counter: std::sync::atomic::AtomicU64,
20    pub pending_async_recorders: std::sync::Mutex<Vec<(u64, Vec<VulkanCommandRecorder>)>>,
21    pub allocator: std::sync::Mutex<allocator::VulkanSuballocator>,
22    pub pipeline_cache: vk::PipelineCache,
23}
24
25impl VulkanDevice {
26    pub(crate) fn find_memory_type(
27        &self,
28        type_filter: u32,
29        required: vk::MemoryPropertyFlags,
30    ) -> Result<(u32, bool), HalError> {
31        let wants_host = required.contains(vk::MemoryPropertyFlags::HOST_VISIBLE);
32        if wants_host {
33            let preferred = required | vk::MemoryPropertyFlags::HOST_COHERENT;
34            for i in 0..self.memory_properties.memory_type_count {
35                if (type_filter & (1 << i)) != 0
36                    && (self.memory_properties.memory_types[i as usize].property_flags & preferred)
37                        == preferred
38                {
39                    return Ok((i, true));
40                }
41            }
42        }
43
44        for i in 0..self.memory_properties.memory_type_count {
45            if (type_filter & (1 << i)) != 0
46                && (self.memory_properties.memory_types[i as usize].property_flags & required)
47                    == required
48            {
49                let is_coherent = (self.memory_properties.memory_types[i as usize].property_flags
50                    & vk::MemoryPropertyFlags::HOST_COHERENT)
51                    == vk::MemoryPropertyFlags::HOST_COHERENT;
52                return Ok((i, is_coherent));
53            }
54        }
55        Err(HalError::OutOfMemory(
56            "Suitable memory type not found".to_string(),
57        ))
58    }
59
60    pub fn wait_idle(&self) -> Result<(), HalError> {
61        unsafe {
62            self.device
63                .device_wait_idle()
64                .map_err(|e| HalError::BackendError(format!("device_wait_idle failed: {e}")))?;
65        }
66        let mut pending = self.pending_async_recorders.lock().unwrap();
67        let mut cache = self.command_recorder_cache.lock().unwrap();
68        for (_, recorders) in pending.drain(..) {
69            for r in recorders {
70                unsafe {
71                    self.device
72                        .reset_command_buffer(
73                            r.command_buffer,
74                            vk::CommandBufferResetFlags::empty(),
75                        )
76                        .ok();
77                }
78                cache.push((r.command_pool, r.command_buffer));
79            }
80        }
81        Ok(())
82    }
83
84    /// Reclaims completely empty memory blocks in the suballocator back to Vulkan.
85    pub fn trim_memory(&self) -> usize {
86        self.allocator.lock().unwrap().trim(&self.device)
87    }
88
89    /// Returns the number of memory blocks currently held by the suballocator.
90    pub fn suballocator_block_count(&self) -> usize {
91        self.allocator.lock().unwrap().block_count()
92    }
93
94    /// Returns the number of empty memory blocks currently held in the suballocator.
95    pub fn suballocator_empty_block_count(&self) -> usize {
96        self.allocator.lock().unwrap().empty_block_count()
97    }
98}
99
100impl Drop for VulkanDevice {
101    fn drop(&mut self) {
102        unsafe {
103            let _ = self.device.device_wait_idle();
104            if let Some(heap) = self.bindless_heap.take() {
105                heap.destroy(&self.device);
106            }
107            self.device
108                .destroy_semaphore(self.internal_frame_semaphore, None);
109            for (pool, _) in self.command_recorder_cache.lock().unwrap().drain(..) {
110                self.device.destroy_command_pool(pool, None);
111            }
112            for (_, recorders) in self.pending_async_recorders.lock().unwrap().drain(..) {
113                for r in recorders {
114                    self.device.destroy_command_pool(r.command_pool, None);
115                }
116            }
117            for fence in self.fence_cache.lock().unwrap().drain(..) {
118                self.device.destroy_fence(fence, None);
119            }
120            self.allocator.lock().unwrap().destroy(&self.device);
121            self.device
122                .destroy_pipeline_cache(self.pipeline_cache, None);
123            self.device.destroy_device(None);
124        }
125    }
126}
127
128unsafe impl Send for VulkanDevice {}
129unsafe impl Sync for VulkanDevice {}