Skip to main content

lgpu_hal/vulkan/
buffer.rs

1//! Vulkan buffer resource management and operations.
2
3use super::VulkanDevice;
4use crate::{BufferDesc, BufferUsage, HalError, MemoryLocation};
5use ash::vk;
6
7pub struct VulkanBuffer {
8    pub(crate) buffer: vk::Buffer,
9    pub(crate) memory: vk::DeviceMemory,
10    pub(crate) block_id: Option<usize>,
11    pub(crate) offset: u64,
12    pub(crate) size: u64,
13    pub(crate) allocated_size: u64,
14    pub(crate) mapped_ptr: Option<*mut u8>,
15    pub(crate) is_coherent: bool,
16    pub(crate) sync_lock: std::sync::Mutex<()>,
17}
18
19unsafe impl Send for VulkanBuffer {}
20unsafe impl Sync for VulkanBuffer {}
21
22impl VulkanDevice {
23    pub fn create_buffer(&self, desc: &BufferDesc) -> Result<VulkanBuffer, HalError> {
24        if desc.size == 0 {
25            return Err(HalError::InvalidParameter(
26                "Buffer size must be greater than 0".to_string(),
27            ));
28        }
29
30        let mut vk_usage = vk::BufferUsageFlags::empty();
31        if desc.usage.contains(BufferUsage::TRANSFER_SRC) {
32            vk_usage |= vk::BufferUsageFlags::TRANSFER_SRC;
33        }
34        if desc.usage.contains(BufferUsage::TRANSFER_DST) {
35            vk_usage |= vk::BufferUsageFlags::TRANSFER_DST;
36        }
37        if desc.usage.contains(BufferUsage::STORAGE) {
38            vk_usage |= vk::BufferUsageFlags::STORAGE_BUFFER;
39        }
40        if desc.usage.contains(BufferUsage::UNIFORM) {
41            vk_usage |= vk::BufferUsageFlags::UNIFORM_BUFFER;
42        }
43        if desc.usage.contains(BufferUsage::VERTEX) {
44            vk_usage |= vk::BufferUsageFlags::VERTEX_BUFFER;
45        }
46        if desc.usage.contains(BufferUsage::INDEX) {
47            vk_usage |= vk::BufferUsageFlags::INDEX_BUFFER;
48        }
49        if desc.usage.contains(BufferUsage::INDIRECT) {
50            vk_usage |= vk::BufferUsageFlags::INDIRECT_BUFFER;
51        }
52
53        let buffer_info = vk::BufferCreateInfo::default()
54            .size(desc.size)
55            .usage(vk_usage)
56            .sharing_mode(vk::SharingMode::EXCLUSIVE);
57
58        let buffer = unsafe {
59            self.device
60                .create_buffer(&buffer_info, None)
61                .map_err(|e| HalError::BackendError(format!("vkCreateBuffer failed: {e}")))?
62        };
63
64        let mem_reqs = unsafe { self.device.get_buffer_memory_requirements(buffer) };
65
66        let mem_props = match desc.memory_location {
67            MemoryLocation::GpuOnly => vk::MemoryPropertyFlags::DEVICE_LOCAL,
68            MemoryLocation::CpuToGpu => vk::MemoryPropertyFlags::HOST_VISIBLE,
69            MemoryLocation::GpuToCpu => vk::MemoryPropertyFlags::HOST_VISIBLE,
70        };
71
72        let (mem_type_index, is_coherent) =
73            self.find_memory_type(mem_reqs.memory_type_bits, mem_props)?;
74        let is_host_visible = desc.memory_location != MemoryLocation::GpuOnly;
75
76        let alloc = self.allocator.lock().unwrap().allocate(
77            &self.device,
78            mem_reqs.size,
79            mem_reqs.alignment,
80            mem_type_index,
81            is_host_visible,
82            is_coherent,
83            super::allocator::AllocationType::Linear,
84        )?;
85
86        unsafe {
87            if let Err(e) = self
88                .device
89                .bind_buffer_memory(buffer, alloc.memory, alloc.offset)
90            {
91                if let Some(block_id) = alloc.block_id {
92                    self.allocator
93                        .lock()
94                        .unwrap()
95                        .free(block_id, alloc.offset, mem_reqs.size);
96                } else {
97                    self.device.free_memory(alloc.memory, None);
98                }
99                self.device.destroy_buffer(buffer, None);
100                return Err(HalError::BackendError(format!(
101                    "vkBindBufferMemory failed: {e}"
102                )));
103            }
104        }
105
106        Ok(VulkanBuffer {
107            buffer,
108            memory: alloc.memory,
109            block_id: alloc.block_id,
110            offset: alloc.offset,
111            size: desc.size,
112            allocated_size: mem_reqs.size,
113            mapped_ptr: alloc.mapped_ptr,
114            is_coherent: alloc.is_coherent,
115            sync_lock: std::sync::Mutex::new(()),
116        })
117    }
118
119    pub fn write_buffer(
120        &self,
121        buffer: &VulkanBuffer,
122        offset: u64,
123        data: &[u8],
124    ) -> Result<(), HalError> {
125        let _guard = buffer.sync_lock.lock().unwrap();
126        let ptr = buffer
127            .mapped_ptr
128            .ok_or_else(|| HalError::MapError("Buffer is not host-mapped".to_string()))?;
129        if offset
130            .checked_add(data.len() as u64)
131            .is_none_or(|end| end > buffer.size)
132        {
133            return Err(HalError::CommandError("Buffer write overflow".to_string()));
134        }
135        unsafe {
136            let dst = ptr.add(offset as usize);
137            std::ptr::copy_nonoverlapping(data.as_ptr(), dst, data.len());
138
139            if !buffer.is_coherent {
140                let atom = self.non_coherent_atom_size.max(1);
141                let mem_offset = buffer.offset + offset;
142                let aligned_offset = (mem_offset / atom) * atom;
143                let end = mem_offset + data.len() as u64;
144                let aligned_end = ((end + atom - 1) / atom) * atom;
145                let aligned_size = aligned_end - aligned_offset;
146
147                let range = vk::MappedMemoryRange::default()
148                    .memory(buffer.memory)
149                    .offset(aligned_offset)
150                    .size(aligned_size);
151                self.device
152                    .flush_mapped_memory_ranges(&[range])
153                    .map_err(|e| {
154                        HalError::MapError(format!("vkFlushMappedMemoryRanges failed: {e}"))
155                    })?;
156            }
157        }
158        Ok(())
159    }
160
161    pub fn read_buffer(
162        &self,
163        buffer: &VulkanBuffer,
164        offset: u64,
165        data: &mut [u8],
166    ) -> Result<(), HalError> {
167        let _guard = buffer.sync_lock.lock().unwrap();
168        let ptr = buffer
169            .mapped_ptr
170            .ok_or_else(|| HalError::MapError("Buffer is not host-mapped".to_string()))?;
171        if offset
172            .checked_add(data.len() as u64)
173            .is_none_or(|end| end > buffer.size)
174        {
175            return Err(HalError::CommandError("Buffer read overflow".to_string()));
176        }
177        unsafe {
178            if !buffer.is_coherent {
179                let atom = self.non_coherent_atom_size.max(1);
180                let mem_offset = buffer.offset + offset;
181                let aligned_offset = (mem_offset / atom) * atom;
182                let end = mem_offset + data.len() as u64;
183                let aligned_end = ((end + atom - 1) / atom) * atom;
184                let aligned_size = aligned_end - aligned_offset;
185
186                let range = vk::MappedMemoryRange::default()
187                    .memory(buffer.memory)
188                    .offset(aligned_offset)
189                    .size(aligned_size);
190                self.device
191                    .invalidate_mapped_memory_ranges(&[range])
192                    .map_err(|e| {
193                        HalError::MapError(format!("vkInvalidateMappedMemoryRanges failed: {e}"))
194                    })?;
195            }
196
197            let src = ptr.add(offset as usize);
198            std::ptr::copy_nonoverlapping(src, data.as_mut_ptr(), data.len());
199        }
200        Ok(())
201    }
202
203    pub fn destroy_buffer(&self, mut buffer: VulkanBuffer) {
204        let _guard = buffer.sync_lock.lock().unwrap();
205        unsafe {
206            if buffer.block_id.is_none() && buffer.mapped_ptr.take().is_some() {
207                self.device.unmap_memory(buffer.memory);
208            } else {
209                buffer.mapped_ptr.take();
210            }
211            self.device.destroy_buffer(buffer.buffer, None);
212            if let Some(block_id) = buffer.block_id {
213                self.allocator
214                    .lock()
215                    .unwrap()
216                    .free(block_id, buffer.offset, buffer.allocated_size);
217            } else {
218                self.device.free_memory(buffer.memory, None);
219            }
220        }
221    }
222
223    pub fn register_bindless_buffer(&self, buffer: &VulkanBuffer) -> Result<u32, HalError> {
224        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
225            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
226        })?;
227        let slot = heap.allocate_buffer_slot()?;
228        heap.write_buffer_descriptor(&self.device, slot, buffer.buffer, buffer.size);
229        Ok(slot)
230    }
231
232    pub fn unregister_bindless_buffer(&self, slot: u32) {
233        if let Some(heap) = &self.bindless_heap {
234            heap.free_buffer_slot(slot);
235        }
236    }
237
238    pub fn write_buffer_descriptors_batch(
239        &self,
240        writes: &[(u32, &VulkanBuffer)],
241    ) -> Result<(), HalError> {
242        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
243            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
244        })?;
245        let raw_writes: Vec<(u32, ash::vk::Buffer, u64)> = writes
246            .iter()
247            .map(|(slot, buf)| (*slot, buf.buffer, buf.size))
248            .collect();
249        heap.write_buffer_descriptors_batch(&self.device, &raw_writes);
250        Ok(())
251    }
252}