1use crate::HalError;
7use ash::vk;
8
9pub const DEFAULT_BLOCK_SIZE: u64 = 32 * 1024 * 1024; pub const SUBALLOCATION_THRESHOLD: u64 = 16 * 1024 * 1024; #[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum AllocationType {
16 Linear,
17 Optimal,
18}
19
20#[derive(Debug)]
21pub struct AllocationResult {
22 pub memory: vk::DeviceMemory,
23 pub offset: u64,
24 pub block_id: Option<usize>,
25 pub mapped_ptr: Option<*mut u8>,
26 pub is_coherent: bool,
27}
28
29pub struct MemoryBlock {
30 pub id: usize,
31 pub memory: vk::DeviceMemory,
32 pub size: u64,
33 pub memory_type_index: u32,
34 pub is_host_visible: bool,
35 pub allocation_type: AllocationType,
36 pub mapped_base: Option<*mut u8>,
37 pub is_coherent: bool,
38 pub free_chunks: Vec<(u64, u64)>, }
40
41unsafe impl Send for MemoryBlock {}
42unsafe impl Sync for MemoryBlock {}
43
44impl MemoryBlock {
45 pub fn allocate(&mut self, size: u64, alignment: u64) -> Option<u64> {
46 let align = alignment.max(1);
47 for i in 0..self.free_chunks.len() {
48 let (chunk_offset, chunk_size) = self.free_chunks[i];
49 let aligned_offset = if align.is_power_of_two() {
50 (chunk_offset + align - 1) & !(align - 1)
51 } else {
52 ((chunk_offset + align - 1) / align) * align
53 };
54 let padding = aligned_offset - chunk_offset;
55 if chunk_size >= padding + size {
56 self.free_chunks.remove(i);
58 if padding > 0 {
59 self.free_chunks.push((chunk_offset, padding));
60 }
61 let remaining = chunk_size - (padding + size);
62 if remaining > 0 {
63 self.free_chunks.push((aligned_offset + size, remaining));
64 }
65 self.free_chunks.sort_unstable_by_key(|&(off, _)| off);
66 return Some(aligned_offset);
67 }
68 }
69 None
70 }
71
72 pub fn free(&mut self, offset: u64, size: u64) {
73 self.free_chunks.push((offset, size));
74 self.free_chunks.sort_unstable_by_key(|&(off, _)| off);
75
76 let mut merged: Vec<(u64, u64)> = Vec::with_capacity(self.free_chunks.len());
78 for chunk in &self.free_chunks {
79 if let Some(last) = merged.last_mut() {
80 if last.0 + last.1 == chunk.0 {
81 last.1 += chunk.1;
82 continue;
83 }
84 }
85 merged.push(*chunk);
86 }
87 self.free_chunks = merged;
88 }
89}
90
91pub struct VulkanSuballocator {
92 blocks: Vec<MemoryBlock>,
93 next_block_id: usize,
94}
95
96impl VulkanSuballocator {
97 pub fn new() -> Self {
98 Self {
99 blocks: Vec::new(),
100 next_block_id: 0,
101 }
102 }
103
104 pub fn allocate(
105 &mut self,
106 device: &ash::Device,
107 size: u64,
108 alignment: u64,
109 memory_type_index: u32,
110 is_host_visible: bool,
111 is_coherent: bool,
112 allocation_type: AllocationType,
113 ) -> Result<AllocationResult, HalError> {
114 if size >= SUBALLOCATION_THRESHOLD {
116 let alloc_info = vk::MemoryAllocateInfo::default()
117 .allocation_size(size)
118 .memory_type_index(memory_type_index);
119
120 let memory = unsafe {
121 device.allocate_memory(&alloc_info, None).map_err(|e| {
122 HalError::OutOfMemory(format!("vkAllocateMemory dedicated failed: {e}"))
123 })?
124 };
125
126 let mapped_ptr = if is_host_visible {
127 let ptr = unsafe {
128 device
129 .map_memory(memory, 0, size, vk::MemoryMapFlags::empty())
130 .map_err(|e| HalError::MapError(e.to_string()))?
131 };
132 Some(ptr as *mut u8)
133 } else {
134 None
135 };
136
137 return Ok(AllocationResult {
138 memory,
139 offset: 0,
140 block_id: None,
141 mapped_ptr,
142 is_coherent,
143 });
144 }
145
146 for block in &mut self.blocks {
148 if block.memory_type_index == memory_type_index
149 && block.is_host_visible == is_host_visible
150 && block.allocation_type == allocation_type
151 {
152 if let Some(offset) = block.allocate(size, alignment) {
153 let mapped_ptr = block
154 .mapped_base
155 .map(|base| unsafe { base.add(offset as usize) });
156 return Ok(AllocationResult {
157 memory: block.memory,
158 offset,
159 block_id: Some(block.id),
160 mapped_ptr,
161 is_coherent: block.is_coherent,
162 });
163 }
164 }
165 }
166
167 let block_size = DEFAULT_BLOCK_SIZE.max(size);
169 let alloc_info = vk::MemoryAllocateInfo::default()
170 .allocation_size(block_size)
171 .memory_type_index(memory_type_index);
172
173 let memory = unsafe {
174 device
175 .allocate_memory(&alloc_info, None)
176 .map_err(|e| HalError::OutOfMemory(format!("vkAllocateMemory block failed: {e}")))?
177 };
178
179 let mapped_base = if is_host_visible {
180 let ptr = unsafe {
181 device
182 .map_memory(memory, 0, block_size, vk::MemoryMapFlags::empty())
183 .map_err(|e| HalError::MapError(e.to_string()))?
184 };
185 Some(ptr as *mut u8)
186 } else {
187 None
188 };
189
190 let block_id = self.next_block_id;
191 self.next_block_id += 1;
192
193 let mut block = MemoryBlock {
194 id: block_id,
195 memory,
196 size: block_size,
197 memory_type_index,
198 is_host_visible,
199 allocation_type,
200 mapped_base,
201 is_coherent,
202 free_chunks: vec![(0, block_size)],
203 };
204
205 let offset = block
206 .allocate(size, alignment)
207 .expect("Fresh block must have sufficient space for allocation");
208
209 let mapped_ptr = block
210 .mapped_base
211 .map(|base| unsafe { base.add(offset as usize) });
212
213 self.blocks.push(block);
214
215 Ok(AllocationResult {
216 memory,
217 offset,
218 block_id: Some(block_id),
219 mapped_ptr,
220 is_coherent,
221 })
222 }
223
224 pub fn free(&mut self, block_id: usize, offset: u64, size: u64) {
225 if let Some(block) = self.blocks.iter_mut().find(|b| b.id == block_id) {
226 block.free(offset, size);
227 }
228 }
229
230 pub fn destroy(&mut self, device: &ash::Device) {
231 unsafe {
232 for block in self.blocks.drain(..) {
233 if block.mapped_base.is_some() {
234 device.unmap_memory(block.memory);
235 }
236 device.free_memory(block.memory, None);
237 }
238 }
239 }
240}