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