1use std::ptr::NonNull;
111
112use erupt::{vk::MemoryMapFlags, vk1_0, vk1_1, DeviceLoader, ExtendableFrom, InstanceLoader};
113use gpu_alloc_types::{
114 AllocationFlags, DeviceMapError, DeviceProperties, MappedMemoryRange, MemoryDevice, MemoryHeap,
115 MemoryPropertyFlags, MemoryType, OutOfMemory,
116};
117use tinyvec::TinyVec;
118
119#[repr(transparent)]
120pub struct EruptMemoryDevice {
121 device: DeviceLoader,
122}
123
124impl EruptMemoryDevice {
125 pub fn wrap(device: &DeviceLoader) -> &Self {
126 unsafe {
127 &*(device as *const DeviceLoader as *const Self)
130 }
131 }
132}
133
134impl AsRef<EruptMemoryDevice> for DeviceLoader {
135 #[inline(always)]
136 fn as_ref(&self) -> &EruptMemoryDevice {
137 EruptMemoryDevice::wrap(self)
138 }
139}
140
141impl AsRef<EruptMemoryDevice> for EruptMemoryDevice {
142 #[inline(always)]
143 fn as_ref(&self) -> &EruptMemoryDevice {
144 self
145 }
146}
147
148impl MemoryDevice<vk1_0::DeviceMemory> for EruptMemoryDevice {
149 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
150 unsafe fn allocate_memory(
151 &self,
152 size: u64,
153 memory_type: u32,
154 flags: AllocationFlags,
155 ) -> Result<vk1_0::DeviceMemory, OutOfMemory> {
156 assert!((flags & !(AllocationFlags::DEVICE_ADDRESS)).is_empty());
157
158 let mut info = vk1_0::MemoryAllocateInfoBuilder::new()
159 .allocation_size(size)
160 .memory_type_index(memory_type);
161
162 let mut info_flags;
163
164 if flags.contains(AllocationFlags::DEVICE_ADDRESS) {
165 info_flags = vk1_1::MemoryAllocateFlagsInfoBuilder::new()
166 .flags(vk1_1::MemoryAllocateFlags::DEVICE_ADDRESS);
167 info = info.extend_from(&mut info_flags);
168 }
169
170 match self.device.allocate_memory(&info, None).result() {
171 Ok(memory) => Ok(memory),
172 Err(vk1_0::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::OutOfDeviceMemory),
173 Err(vk1_0::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(OutOfMemory::OutOfHostMemory),
174 Err(vk1_0::Result::ERROR_TOO_MANY_OBJECTS) => panic!("Too many objects"),
175 Err(err) => panic!("Unexpected Vulkan error: `{}`", err),
176 }
177 }
178
179 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
180 unsafe fn deallocate_memory(&self, memory: vk1_0::DeviceMemory) {
181 self.device.free_memory(memory, None);
182 }
183
184 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
185 unsafe fn map_memory(
186 &self,
187 memory: &mut vk1_0::DeviceMemory,
188 offset: u64,
189 size: u64,
190 ) -> Result<NonNull<u8>, DeviceMapError> {
191 match self
192 .device
193 .map_memory(*memory, offset, size, MemoryMapFlags::empty())
194 .result()
195 {
196 Ok(ptr) => {
197 Ok(NonNull::new(ptr as *mut u8)
198 .expect("Pointer to memory mapping must not be null"))
199 }
200 Err(vk1_0::Result::ERROR_OUT_OF_DEVICE_MEMORY) => {
201 Err(DeviceMapError::OutOfDeviceMemory)
202 }
203 Err(vk1_0::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(DeviceMapError::OutOfHostMemory),
204 Err(vk1_0::Result::ERROR_MEMORY_MAP_FAILED) => Err(DeviceMapError::MapFailed),
205 Err(err) => panic!("Unexpected Vulkan error: `{}`", err),
206 }
207 }
208
209 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
210 unsafe fn unmap_memory(&self, memory: &mut vk1_0::DeviceMemory) {
211 self.device.unmap_memory(*memory);
212 }
213
214 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
215 unsafe fn invalidate_memory_ranges(
216 &self,
217 ranges: &[MappedMemoryRange<'_, vk1_0::DeviceMemory>],
218 ) -> Result<(), OutOfMemory> {
219 self.device
220 .invalidate_mapped_memory_ranges(
221 &ranges
222 .iter()
223 .map(|range| {
224 vk1_0::MappedMemoryRangeBuilder::new()
225 .memory(*range.memory)
226 .offset(range.offset)
227 .size(range.size)
228 })
229 .collect::<TinyVec<[_; 4]>>(),
230 )
231 .result()
232 .map_err(|err| match err {
233 vk1_0::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
234 vk1_0::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
235 err => panic!("Unexpected Vulkan error: `{}`", err),
236 })
237 }
238
239 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
240 unsafe fn flush_memory_ranges(
241 &self,
242 ranges: &[MappedMemoryRange<'_, vk1_0::DeviceMemory>],
243 ) -> Result<(), OutOfMemory> {
244 self.device
245 .flush_mapped_memory_ranges(
246 &ranges
247 .iter()
248 .map(|range| {
249 vk1_0::MappedMemoryRangeBuilder::new()
250 .memory(*range.memory)
251 .offset(range.offset)
252 .size(range.size)
253 })
254 .collect::<TinyVec<[_; 4]>>(),
255 )
256 .result()
257 .map_err(|err| match err {
258 vk1_0::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
259 vk1_0::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
260 err => panic!("Unexpected Vulkan error: `{}`", err),
261 })
262 }
263}
264
265pub unsafe fn device_properties(
275 instance: &InstanceLoader,
276 physical_device: vk1_0::PhysicalDevice,
277) -> Result<DeviceProperties<'static>, vk1_0::Result> {
278 use {
279 erupt::{
280 extensions::khr_buffer_device_address::KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
281 vk1_1::PhysicalDeviceFeatures2, vk1_2::PhysicalDeviceBufferDeviceAddressFeatures,
282 },
283 std::ffi::CStr,
284 };
285
286 let limits = instance
287 .get_physical_device_properties(physical_device)
288 .limits;
289
290 let memory_properties = instance.get_physical_device_memory_properties(physical_device);
291
292 let buffer_device_address =
293 if instance.enabled().vk1_1 || instance.enabled().khr_get_physical_device_properties2 {
294 let mut bda_features_available = instance.enabled().vk1_2;
295
296 if !bda_features_available {
297 let extensions = instance
298 .enumerate_device_extension_properties(physical_device, None, None)
299 .result()?;
300
301 bda_features_available = extensions.iter().any(|ext| {
302 let name = CStr::from_bytes_with_nul({
303 std::slice::from_raw_parts(
304 ext.extension_name.as_ptr() as *const _,
305 ext.extension_name.len(),
306 )
307 });
308 if let Ok(name) = name {
309 name == { CStr::from_ptr(KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) }
310 } else {
311 false
312 }
313 });
314 }
315
316 if bda_features_available {
317 let features = PhysicalDeviceFeatures2::default().into_builder();
318 let mut bda_features = PhysicalDeviceBufferDeviceAddressFeatures::default();
319 let mut features = features.extend_from(&mut bda_features);
320 instance.get_physical_device_features2(physical_device, &mut features);
321 bda_features.buffer_device_address != 0
322 } else {
323 false
324 }
325 } else {
326 false
327 };
328
329 Ok(DeviceProperties {
330 max_memory_allocation_count: limits.max_memory_allocation_count,
331 max_memory_allocation_size: u64::max_value(), non_coherent_atom_size: limits.non_coherent_atom_size,
333 memory_types: memory_properties.memory_types
334 [..memory_properties.memory_type_count as usize]
335 .iter()
336 .map(|memory_type| MemoryType {
337 props: memory_properties_from_erupt(memory_type.property_flags),
338 heap: memory_type.heap_index,
339 })
340 .collect(),
341 memory_heaps: memory_properties.memory_heaps
342 [..memory_properties.memory_heap_count as usize]
343 .iter()
344 .map(|&memory_heap| MemoryHeap {
345 size: memory_heap.size,
346 })
347 .collect(),
348 buffer_device_address,
349 })
350}
351
352pub fn memory_properties_from_erupt(props: vk1_0::MemoryPropertyFlags) -> MemoryPropertyFlags {
353 let mut result = MemoryPropertyFlags::empty();
354 if props.contains(vk1_0::MemoryPropertyFlags::DEVICE_LOCAL) {
355 result |= MemoryPropertyFlags::DEVICE_LOCAL;
356 }
357 if props.contains(vk1_0::MemoryPropertyFlags::HOST_VISIBLE) {
358 result |= MemoryPropertyFlags::HOST_VISIBLE;
359 }
360 if props.contains(vk1_0::MemoryPropertyFlags::HOST_COHERENT) {
361 result |= MemoryPropertyFlags::HOST_COHERENT;
362 }
363 if props.contains(vk1_0::MemoryPropertyFlags::HOST_CACHED) {
364 result |= MemoryPropertyFlags::HOST_CACHED;
365 }
366 if props.contains(vk1_0::MemoryPropertyFlags::LAZILY_ALLOCATED) {
367 result |= MemoryPropertyFlags::LAZILY_ALLOCATED;
368 }
369 result
370}
371
372pub fn memory_properties_to_erupt(props: MemoryPropertyFlags) -> vk1_0::MemoryPropertyFlags {
373 let mut result = vk1_0::MemoryPropertyFlags::empty();
374 if props.contains(MemoryPropertyFlags::DEVICE_LOCAL) {
375 result |= vk1_0::MemoryPropertyFlags::DEVICE_LOCAL;
376 }
377 if props.contains(MemoryPropertyFlags::HOST_VISIBLE) {
378 result |= vk1_0::MemoryPropertyFlags::HOST_VISIBLE;
379 }
380 if props.contains(MemoryPropertyFlags::HOST_COHERENT) {
381 result |= vk1_0::MemoryPropertyFlags::HOST_COHERENT;
382 }
383 if props.contains(MemoryPropertyFlags::HOST_CACHED) {
384 result |= vk1_0::MemoryPropertyFlags::HOST_CACHED;
385 }
386 if props.contains(MemoryPropertyFlags::LAZILY_ALLOCATED) {
387 result |= vk1_0::MemoryPropertyFlags::LAZILY_ALLOCATED;
388 }
389 result
390}