// SPDX-License-Identifier: MIT OR Apache-2.0 // Author: Zakarum // https://github.com/zakarumych/gpu-alloc use { ash::{ Device, Instance, vk::{ self, PhysicalDeviceFeatures2, PhysicalDeviceProperties2, PhysicalDeviceVulkan11Properties, }, }, gpu_alloc_types::{ AllocationFlags, DeviceMapError, DeviceProperties, MappedMemoryRange, MemoryDevice, MemoryHeap, MemoryPropertyFlags, MemoryType, OutOfMemory, }, smallvec::SmallVec, std::{mem, ptr::NonNull}, }; #[repr(transparent)] pub struct AshMemoryDevice { device: Device, } impl AshMemoryDevice { pub fn wrap(device: &Device) -> &Self { unsafe { mem::transmute::<&Device, &Self>(device) } } } impl MemoryDevice for AshMemoryDevice { unsafe fn allocate_memory( &self, size: u64, memory_type: u32, flags: AllocationFlags, ) -> Result { assert!((flags & !AllocationFlags::DEVICE_ADDRESS).is_empty()); let mut info = vk::MemoryAllocateInfo::default() .allocation_size(size) .memory_type_index(memory_type); let mut info_flags; if flags.contains(AllocationFlags::DEVICE_ADDRESS) { info_flags = vk::MemoryAllocateFlagsInfo::default() .flags(vk::MemoryAllocateFlags::DEVICE_ADDRESS); info = info.push_next(&mut info_flags); } let res = unsafe { self.device.allocate_memory(&info, None) }; match res { Ok(memory) => Ok(memory), Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::OutOfDeviceMemory), Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(OutOfMemory::OutOfHostMemory), Err(vk::Result::ERROR_TOO_MANY_OBJECTS) => panic!("Too many objects"), Err(err) => panic!("Unexpected Vulkan error: `{}`", err), } } unsafe fn deallocate_memory(&self, memory: vk::DeviceMemory) { unsafe { self.device.free_memory(memory, None); } } unsafe fn map_memory( &self, memory: &mut vk::DeviceMemory, offset: u64, size: u64, ) -> Result, DeviceMapError> { let res = unsafe { self.device .map_memory(*memory, offset, size, vk::MemoryMapFlags::empty()) }; match res { Ok(ptr) => { Ok(NonNull::new(ptr as *mut u8) .expect("Pointer to memory mapping must not be null")) } Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(DeviceMapError::OutOfDeviceMemory), Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(DeviceMapError::OutOfHostMemory), Err(vk::Result::ERROR_MEMORY_MAP_FAILED) => Err(DeviceMapError::MapFailed), Err(err) => panic!("Unexpected Vulkan error: `{}`", err), } } unsafe fn unmap_memory(&self, memory: &mut vk::DeviceMemory) { unsafe { self.device.unmap_memory(*memory); } } unsafe fn invalidate_memory_ranges( &self, ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>], ) -> Result<(), OutOfMemory> { unsafe { self.device .invalidate_mapped_memory_ranges(&map_ranges(ranges)) .map_err(|err| match err { vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory, vk::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory, err => panic!("Unexpected Vulkan error: `{}`", err), }) } } unsafe fn flush_memory_ranges( &self, ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>], ) -> Result<(), OutOfMemory> { unsafe { self.device .flush_mapped_memory_ranges(&map_ranges(ranges)) .map_err(|err| match err { vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory, vk::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory, err => panic!("Unexpected Vulkan error: `{}`", err), }) } } } pub unsafe fn device_properties( instance: &Instance, physical_device: vk::PhysicalDevice, ) -> Result, vk::Result> { let mut properties_11 = PhysicalDeviceVulkan11Properties::default(); let mut properties = PhysicalDeviceProperties2::default().push_next(&mut properties_11); unsafe { instance.get_physical_device_properties2(physical_device, &mut properties); }; let limits = properties.properties.limits; let memory_properties = unsafe { instance.get_physical_device_memory_properties(physical_device) }; let buffer_device_address = { let mut bda_features = vk::PhysicalDeviceBufferDeviceAddressFeatures::default(); let mut features = PhysicalDeviceFeatures2::default().push_next(&mut bda_features); unsafe { instance.get_physical_device_features2(physical_device, &mut features); } bda_features.buffer_device_address != 0 }; Ok(DeviceProperties { max_memory_allocation_count: limits.max_memory_allocation_count, max_memory_allocation_size: properties_11.max_memory_allocation_size, non_coherent_atom_size: limits.non_coherent_atom_size, memory_types: memory_properties.memory_types [..memory_properties.memory_type_count as usize] .iter() .map(|memory_type| MemoryType { props: memory_properties_from_ash(memory_type.property_flags), heap: memory_type.heap_index, }) .collect(), memory_heaps: memory_properties.memory_heaps [..memory_properties.memory_heap_count as usize] .iter() .map(|&memory_heap| MemoryHeap { size: memory_heap.size, }) .collect(), buffer_device_address, }) } fn memory_properties_from_ash(props: vk::MemoryPropertyFlags) -> MemoryPropertyFlags { let mut result = MemoryPropertyFlags::empty(); if props.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) { result |= MemoryPropertyFlags::DEVICE_LOCAL; } if props.contains(vk::MemoryPropertyFlags::HOST_VISIBLE) { result |= MemoryPropertyFlags::HOST_VISIBLE; } if props.contains(vk::MemoryPropertyFlags::HOST_COHERENT) { result |= MemoryPropertyFlags::HOST_COHERENT; } if props.contains(vk::MemoryPropertyFlags::HOST_CACHED) { result |= MemoryPropertyFlags::HOST_CACHED; } if props.contains(vk::MemoryPropertyFlags::LAZILY_ALLOCATED) { result |= MemoryPropertyFlags::LAZILY_ALLOCATED; } result } fn map_ranges( ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>], ) -> SmallVec<[vk::MappedMemoryRange<'static>; 4]> { ranges .iter() .map(|range| { vk::MappedMemoryRange::default() .memory(*range.memory) .offset(range.offset) .size(range.size) }) .collect() }