lgpu_hal/vulkan/
buffer.rs1use 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}