gpu_alloc_vulkanalia/
lib.rs

1use std::ptr::NonNull;
2
3use gpu_alloc_types::{
4    AllocationFlags, DeviceMapError, DeviceProperties, MappedMemoryRange, MemoryDevice, MemoryHeap,
5    MemoryPropertyFlags, MemoryType, OutOfMemory,
6};
7use smallvec::SmallVec;
8use vulkanalia::prelude::v1_0::*;
9use vulkanalia::vk::InstanceV1_1;
10
11/// Vulkan device extension trait which wraps its reference into memory device.
12pub trait AsMemoryDevice {
13    /// Returns a [`MemoryDevice`] wrapper.
14    fn as_memory_device(&self) -> &VulkanaliaMemoryDevice;
15}
16
17impl AsMemoryDevice for Device {
18    fn as_memory_device(&self) -> &VulkanaliaMemoryDevice {
19        VulkanaliaMemoryDevice::wrap(self)
20    }
21}
22
23/// A wrapper around Vulkan device which implements [`MemoryDevice`].
24#[repr(transparent)]
25pub struct VulkanaliaMemoryDevice {
26    device: Device,
27}
28
29impl VulkanaliaMemoryDevice {
30    pub fn wrap(device: &Device) -> &Self {
31        unsafe {
32            // SAFETY: `VulkanaliaMemoryDevice` has the same layout as `Device`
33            &*(device as *const Device).cast::<Self>()
34        }
35    }
36}
37
38impl MemoryDevice<vk::DeviceMemory> for VulkanaliaMemoryDevice {
39    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
40    unsafe fn allocate_memory(
41        &self,
42        size: u64,
43        memory_type: u32,
44        flags: AllocationFlags,
45    ) -> Result<vk::DeviceMemory, OutOfMemory> {
46        assert!((flags & !(AllocationFlags::DEVICE_ADDRESS)).is_empty());
47
48        let mut info = vk::MemoryAllocateInfo::builder()
49            .allocation_size(size)
50            .memory_type_index(memory_type);
51
52        let mut info_flags;
53
54        if flags.contains(AllocationFlags::DEVICE_ADDRESS) {
55            info_flags = vk::MemoryAllocateFlagsInfo::builder()
56                .flags(vk::MemoryAllocateFlags::DEVICE_ADDRESS);
57            info = info.push_next(&mut info_flags);
58        }
59
60        match self.device.allocate_memory(&info, None) {
61            Ok(memory) => Ok(memory),
62            Err(vk::ErrorCode::OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::OutOfDeviceMemory),
63            Err(vk::ErrorCode::OUT_OF_HOST_MEMORY) => Err(OutOfMemory::OutOfHostMemory),
64            Err(e) => panic!("Unexpected Vulkan error: {e}"),
65        }
66    }
67
68    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
69    unsafe fn deallocate_memory(&self, memory: vk::DeviceMemory) {
70        self.device.free_memory(memory, None);
71    }
72
73    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
74    unsafe fn map_memory(
75        &self,
76        memory: &mut vk::DeviceMemory,
77        offset: u64,
78        size: u64,
79    ) -> Result<std::ptr::NonNull<u8>, DeviceMapError> {
80        match self
81            .device
82            .map_memory(*memory, offset, size, vk::MemoryMapFlags::empty())
83        {
84            Ok(ptr) => {
85                Ok(NonNull::new(ptr as *mut u8)
86                    .expect("Pointer to memory mapping must not be null"))
87            }
88            Err(vk::ErrorCode::OUT_OF_DEVICE_MEMORY) => Err(DeviceMapError::OutOfDeviceMemory),
89            Err(vk::ErrorCode::OUT_OF_HOST_MEMORY) => Err(DeviceMapError::OutOfHostMemory),
90            Err(vk::ErrorCode::MEMORY_MAP_FAILED) => Err(DeviceMapError::MapFailed),
91            Err(e) => panic!("Unexpected Vulkan error: {e}"),
92        }
93    }
94
95    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
96    unsafe fn unmap_memory(&self, memory: &mut vk::DeviceMemory) {
97        self.device.unmap_memory(*memory);
98    }
99
100    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
101    unsafe fn invalidate_memory_ranges(
102        &self,
103        ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>],
104    ) -> Result<(), OutOfMemory> {
105        self.device
106            .invalidate_mapped_memory_ranges(
107                &ranges
108                    .iter()
109                    .map(|range| {
110                        vk::MappedMemoryRange::builder()
111                            .memory(*range.memory)
112                            .offset(range.offset)
113                            .size(range.size)
114                    })
115                    .collect::<SmallVec<[_; 4]>>(),
116            )
117            .map_err(|e| match e {
118                vk::ErrorCode::OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
119                vk::ErrorCode::OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
120                e => panic!("Unexpected Vulkan error: {e}"),
121            })
122    }
123
124    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
125    unsafe fn flush_memory_ranges(
126        &self,
127        ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>],
128    ) -> Result<(), OutOfMemory> {
129        self.device
130            .flush_mapped_memory_ranges(
131                &ranges
132                    .iter()
133                    .map(|range| {
134                        vk::MappedMemoryRange::builder()
135                            .memory(*range.memory)
136                            .offset(range.offset)
137                            .size(range.size)
138                    })
139                    .collect::<SmallVec<[_; 4]>>(),
140            )
141            .map_err(|e| match e {
142                vk::ErrorCode::OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
143                vk::ErrorCode::OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
144                e => panic!("Unexpected Vulkan error: {e}"),
145            })
146    }
147}
148
149/// Collects device properties from vulkanalia's `Instance` for the specified
150/// physical device, required to create `GpuAllocator`.
151///
152///
153/// # Safety
154///
155/// The following must be true:
156/// - `version` must not be higher than the `api_version` of the `instance`.
157/// - `physical_device` must be queried from an [`Instance`] associated with this `instance`.
158/// - Even if returned properties' field `buffer_device_address` is set to true,
159///   feature `PhysicalDeviceBufferDeviceAddressFeatures::buffer_derive_address`
160///   must be enabled explicitly on device creation and extension "VK_KHR_buffer_device_address"
161///   for Vulkan prior 1.2.
162///   Otherwise the field must be set to false before passing to `GpuAllocator::new`.
163pub unsafe fn device_properties(
164    instance: &Instance,
165    version: u32,
166    physical_device: vk::PhysicalDevice,
167) -> VkResult<DeviceProperties<'static>> {
168    struct ExtInfo {
169        buffer_device_address: bool,
170        max_memory_allocation_size: u64,
171    }
172
173    let memory_properties = instance.get_physical_device_memory_properties(physical_device);
174
175    // Determine what to fetch by instance version and device features
176    let (query_props, query_features) = 'query: {
177        let mut required_extensions: [_; 2] = [
178            Some(&vk::KHR_MAINTENANCE3_EXTENSION.name),
179            Some(&vk::KHR_BUFFER_DEVICE_ADDRESS_EXTENSION.name),
180        ];
181
182        match vk::version_minor(version) {
183            0 => {
184                if !instance
185                    .extensions()
186                    .contains(&vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name)
187                {
188                    // Required devices extensions cannot be queried
189                    break 'query (false, false);
190                }
191            }
192            1 => {
193                // `max_memory_allocation_size` is mandatory since 1.1
194                required_extensions[1] = None;
195            }
196            _ => {
197                // `PhysicalDeviceBufferDeviceAddressFeatures` is mandatory since 1.2
198                required_extensions = [None, None];
199            }
200        }
201
202        if required_extensions.iter().any(Option::is_some) {
203            let extensions =
204                instance.enumerate_device_extension_properties(physical_device, None)?;
205
206            // Check whether all required extensions are supported
207            let mut to_find = required_extensions.len();
208            'extensions: for extension in extensions {
209                if to_find == 0 {
210                    break 'extensions;
211                }
212
213                for required in required_extensions.iter_mut() {
214                    if let Some(name) = *required {
215                        if name == &extension.extension_name {
216                            *required = None;
217                            to_find -= 1;
218                            continue 'extensions;
219                        }
220                    }
221                }
222            }
223
224            let [limits_ext, bda_ext] = required_extensions;
225            (limits_ext.is_none(), bda_ext.is_none())
226        } else {
227            (true, true)
228        }
229    };
230
231    let mut ext_info = ExtInfo {
232        buffer_device_address: false,
233        max_memory_allocation_size: u64::MAX,
234    };
235
236    // Query physical device properties
237    let limits = if query_props {
238        let mut properties = vk::PhysicalDeviceProperties2::builder();
239        let mut maintenance3 = vk::PhysicalDeviceMaintenance3Properties::builder();
240        properties = properties.push_next(&mut maintenance3);
241        instance.get_physical_device_properties2(physical_device, &mut properties);
242
243        let limits = properties.properties.limits;
244        ext_info.max_memory_allocation_size = maintenance3.max_memory_allocation_size;
245        limits
246    } else {
247        instance
248            .get_physical_device_properties(physical_device)
249            .limits
250    };
251
252    // Query physical device features
253    if query_features {
254        let mut features = vk::PhysicalDeviceFeatures2::builder();
255        let mut bda_features = vk::PhysicalDeviceBufferDeviceAddressFeatures::builder();
256        features = features.push_next(&mut bda_features);
257        instance.get_physical_device_features2(physical_device, &mut features);
258
259        ext_info.buffer_device_address = bda_features.buffer_device_address != 0;
260    };
261
262    // Make device properties
263    Ok(DeviceProperties {
264        memory_types: memory_properties.memory_types
265            [..memory_properties.memory_type_count as usize]
266            .iter()
267            .map(|memory_type| MemoryType {
268                props: memory_properties_from(memory_type.property_flags),
269                heap: memory_type.heap_index,
270            })
271            .collect(),
272        memory_heaps: memory_properties.memory_heaps
273            [..memory_properties.memory_heap_count as usize]
274            .iter()
275            .map(|memory_heap| MemoryHeap {
276                size: memory_heap.size,
277            })
278            .collect(),
279        max_memory_allocation_count: limits.max_memory_allocation_count,
280        max_memory_allocation_size: ext_info.max_memory_allocation_size,
281        non_coherent_atom_size: limits.non_coherent_atom_size,
282        buffer_device_address: ext_info.buffer_device_address,
283    })
284}
285
286/// Maps `vulkanalia`'s `MemoryPropertyFlags` to `gpu-alloc-types`.
287pub fn memory_properties_from(props: vk::MemoryPropertyFlags) -> MemoryPropertyFlags {
288    let mut result = MemoryPropertyFlags::empty();
289    if props.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) {
290        result |= MemoryPropertyFlags::DEVICE_LOCAL;
291    }
292    if props.contains(vk::MemoryPropertyFlags::HOST_VISIBLE) {
293        result |= MemoryPropertyFlags::HOST_VISIBLE;
294    }
295    if props.contains(vk::MemoryPropertyFlags::HOST_COHERENT) {
296        result |= MemoryPropertyFlags::HOST_COHERENT;
297    }
298    if props.contains(vk::MemoryPropertyFlags::HOST_CACHED) {
299        result |= MemoryPropertyFlags::HOST_CACHED;
300    }
301    if props.contains(vk::MemoryPropertyFlags::LAZILY_ALLOCATED) {
302        result |= MemoryPropertyFlags::LAZILY_ALLOCATED;
303    }
304    result
305}
306
307/// Maps `gpu-alloc-types`' `MemoryPropertyFlags` to `vulkanalia`.
308pub fn memory_properties_to(props: MemoryPropertyFlags) -> vk::MemoryPropertyFlags {
309    let mut result = vk::MemoryPropertyFlags::empty();
310    if props.contains(MemoryPropertyFlags::DEVICE_LOCAL) {
311        result |= vk::MemoryPropertyFlags::DEVICE_LOCAL;
312    }
313    if props.contains(MemoryPropertyFlags::HOST_VISIBLE) {
314        result |= vk::MemoryPropertyFlags::HOST_VISIBLE;
315    }
316    if props.contains(MemoryPropertyFlags::HOST_COHERENT) {
317        result |= vk::MemoryPropertyFlags::HOST_COHERENT;
318    }
319    if props.contains(MemoryPropertyFlags::HOST_CACHED) {
320        result |= vk::MemoryPropertyFlags::HOST_CACHED;
321    }
322    if props.contains(MemoryPropertyFlags::LAZILY_ALLOCATED) {
323        result |= vk::MemoryPropertyFlags::LAZILY_ALLOCATED;
324    }
325    result
326}