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.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}