gpu_alloc_erupt/
lib.rs

1//!
2//! # Erupt backend for `gpu-alloc`
3//!  
4//! # Usage example
5//!
6//! ```ignore
7//! use {
8//!     erupt::{vk1_0, DeviceLoader, EntryLoader, InstanceLoader},
9//!     gpu_alloc::{Config, GpuAllocator, Request, UsageFlags},
10//!     gpu_alloc_erupt::{device_properties, EruptMemoryDevice},
11//!     std::ffi::CStr,
12//! };
13//!
14//! fn main() -> eyre::Result<()> {
15//!     color_eyre::install()?;
16//!
17//!     let entry = EntryLoader::new()?;
18//!
19//!     let instance = unsafe {
20//!         InstanceLoader::new(
21//!             &entry,
22//!             &vk1_0::InstanceCreateInfo::default()
23//!                 .into_builder()
24//!                 .application_info(
25//!                     &vk1_0::ApplicationInfo::default()
26//!                         .into_builder()
27//!                         .engine_name(CStr::from_bytes_with_nul(b"GpuAlloc\0").unwrap())
28//!                         .engine_version(1)
29//!                         .application_name(CStr::from_bytes_with_nul(b"GpuAllocApp\0").unwrap())
30//!                         .application_version(1)
31//!                         .api_version(entry.instance_version()),
32//!                 ),
33//!             None,
34//!         )
35//!     }?;
36//!
37//!     let physical_devices = unsafe { instance.enumerate_physical_devices(None) }.result()?;
38//!     let physical_device = physical_devices[0];
39//!
40//!     let props = unsafe { device_properties(&instance, physical_device) }?;
41//!
42//!     let device = unsafe {
43//!         DeviceLoader::new(
44//!             &instance,
45//!             physical_device,
46//!             &vk1_0::DeviceCreateInfoBuilder::new().queue_create_infos(&[
47//!                 vk1_0::DeviceQueueCreateInfoBuilder::new()
48//!                     .queue_family_index(0)
49//!                     .queue_priorities(&[0f32]),
50//!             ]),
51//!             None,
52//!         )
53//!     }?;
54//!
55//!     let config = Config::i_am_potato();
56//!
57//!     let mut allocator = GpuAllocator::new(config, props);
58//!
59//!     let mut block = unsafe {
60//!         allocator.alloc(
61//!             EruptMemoryDevice::wrap(&device),
62//!             Request {
63//!                 size: 10,
64//!                 align_mask: 1,
65//!                 usage: UsageFlags::HOST_ACCESS,
66//!                 memory_types: !0,
67//!             },
68//!         )
69//!     }?;
70//!
71//!     unsafe {
72//!         block.write_bytes(
73//!             EruptMemoryDevice::wrap(&device),
74//!             0,
75//!             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
76//!         )
77//!     }?;
78//!
79//!     unsafe { allocator.dealloc(EruptMemoryDevice::wrap(&device), block) }
80//!
81//!     Ok(())
82//! }
83//! ```
84//!
85
86use 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            // Safe because `Self` is `repr(transparent)`
104            // with only field being `DeviceLoader`.
105            &*(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
227/// Returns `DeviceProperties` from erupt's `InstanceLoader` for specified `PhysicalDevice`, required to create `GpuAllocator`.
228///
229/// # Safety
230///
231/// `physical_device` must be queried from `Instance` associated with this `instance`.
232/// Even if returned properties' field `buffer_device_address` is set to true,
233/// feature `PhysicalDeviceBufferDeviceAddressFeatures::buffer_derive_address`  must be enabled explicitly on device creation
234/// and extension "VK_KHR_buffer_device_address" for Vulkan prior 1.2.
235/// Otherwise the field must be set to false before passing to `GpuAllocator::new`.
236pub 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(), // FIXME: Can query this information if instance is v1.1
294        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}