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.lock().unwrap().free(
93                        block_id,
94                        alloc.offset,
95                        mem_reqs.size,
96                        &self.device,
97                    );
98                } else {
99                    self.device.free_memory(alloc.memory, None);
100                }
101                self.device.destroy_buffer(buffer, None);
102                return Err(HalError::BackendError(format!(
103                    "vkBindBufferMemory failed: {e}"
104                )));
105            }
106        }
107
108        Ok(VulkanBuffer {
109            buffer,
110            memory: alloc.memory,
111            block_id: alloc.block_id,
112            offset: alloc.offset,
113            size: desc.size,
114            allocated_size: mem_reqs.size,
115            mapped_ptr: alloc.mapped_ptr,
116            is_coherent: alloc.is_coherent,
117            sync_lock: std::sync::Mutex::new(()),
118        })
119    }
120
121    pub fn write_buffer(
122        &self,
123        buffer: &VulkanBuffer,
124        offset: u64,
125        data: &[u8],
126    ) -> Result<(), HalError> {
127        let _guard = buffer.sync_lock.lock().unwrap();
128        let ptr = buffer
129            .mapped_ptr
130            .ok_or_else(|| HalError::MapError("Buffer is not host-mapped".to_string()))?;
131        if offset
132            .checked_add(data.len() as u64)
133            .is_none_or(|end| end > buffer.size)
134        {
135            return Err(HalError::CommandError("Buffer write overflow".to_string()));
136        }
137        unsafe {
138            let dst = ptr.add(offset as usize);
139            std::ptr::copy_nonoverlapping(data.as_ptr(), dst, data.len());
140
141            if !buffer.is_coherent {
142                let atom = self.non_coherent_atom_size.max(1);
143                let mem_offset = buffer.offset + offset;
144                let aligned_offset = (mem_offset / atom) * atom;
145                let end = mem_offset + data.len() as u64;
146                let aligned_end = ((end + atom - 1) / atom) * atom;
147                let aligned_size = aligned_end - aligned_offset;
148
149                let range = vk::MappedMemoryRange::default()
150                    .memory(buffer.memory)
151                    .offset(aligned_offset)
152                    .size(aligned_size);
153                self.device
154                    .flush_mapped_memory_ranges(&[range])
155                    .map_err(|e| {
156                        HalError::MapError(format!("vkFlushMappedMemoryRanges failed: {e}"))
157                    })?;
158            }
159        }
160        Ok(())
161    }
162
163    pub fn read_buffer(
164        &self,
165        buffer: &VulkanBuffer,
166        offset: u64,
167        data: &mut [u8],
168    ) -> Result<(), HalError> {
169        let _guard = buffer.sync_lock.lock().unwrap();
170        let ptr = buffer
171            .mapped_ptr
172            .ok_or_else(|| HalError::MapError("Buffer is not host-mapped".to_string()))?;
173        if offset
174            .checked_add(data.len() as u64)
175            .is_none_or(|end| end > buffer.size)
176        {
177            return Err(HalError::CommandError("Buffer read overflow".to_string()));
178        }
179        unsafe {
180            if !buffer.is_coherent {
181                let atom = self.non_coherent_atom_size.max(1);
182                let mem_offset = buffer.offset + offset;
183                let aligned_offset = (mem_offset / atom) * atom;
184                let end = mem_offset + data.len() as u64;
185                let aligned_end = ((end + atom - 1) / atom) * atom;
186                let aligned_size = aligned_end - aligned_offset;
187
188                let range = vk::MappedMemoryRange::default()
189                    .memory(buffer.memory)
190                    .offset(aligned_offset)
191                    .size(aligned_size);
192                self.device
193                    .invalidate_mapped_memory_ranges(&[range])
194                    .map_err(|e| {
195                        HalError::MapError(format!("vkInvalidateMappedMemoryRanges failed: {e}"))
196                    })?;
197            }
198
199            let src = ptr.add(offset as usize);
200            std::ptr::copy_nonoverlapping(src, data.as_mut_ptr(), data.len());
201        }
202        Ok(())
203    }
204
205    pub fn destroy_buffer(&self, mut buffer: VulkanBuffer) {
206        let _guard = buffer.sync_lock.lock().unwrap();
207        unsafe {
208            if buffer.block_id.is_none() && buffer.mapped_ptr.take().is_some() {
209                self.device.unmap_memory(buffer.memory);
210            } else {
211                buffer.mapped_ptr.take();
212            }
213            self.device.destroy_buffer(buffer.buffer, None);
214            if let Some(block_id) = buffer.block_id {
215                self.allocator.lock().unwrap().free(
216                    block_id,
217                    buffer.offset,
218                    buffer.allocated_size,
219                    &self.device,
220                );
221            } else {
222                self.device.free_memory(buffer.memory, None);
223            }
224        }
225    }
226
227    pub fn register_bindless_buffer(&self, buffer: &VulkanBuffer) -> Result<u32, HalError> {
228        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
229            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
230        })?;
231        let slot = heap.allocate_buffer_slot()?;
232        heap.write_buffer_descriptor(&self.device, slot, buffer.buffer, buffer.size);
233        Ok(slot)
234    }
235
236    pub fn unregister_bindless_buffer(&self, slot: u32) {
237        if let Some(heap) = &self.bindless_heap {
238            heap.free_buffer_slot(slot);
239        }
240    }
241
242    pub fn write_buffer_descriptors_batch(
243        &self,
244        writes: &[(u32, &VulkanBuffer)],
245    ) -> Result<(), HalError> {
246        let heap = self.bindless_heap.as_ref().ok_or_else(|| {
247            HalError::BackendError("Bindless descriptor heap is not enabled".to_string())
248        })?;
249        let raw_writes: Vec<(u32, ash::vk::Buffer, u64)> = writes
250            .iter()
251            .map(|(slot, buf)| (*slot, buf.buffer, buf.size))
252            .collect();
253        heap.write_buffer_descriptors_batch(&self.device, &raw_writes);
254        Ok(())
255    }
256}