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