Skip to main content

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//!     // the `erupt::DeviceLoader` also implements `AsRef<EruptMemoryDevice>`
82//!     // you can pass a reference of `erupt::DeviceLoader` directly as argument
83//!     let mut block = unsafe {
84//!         allocator.alloc(
85//!             &device,
86//!             Request {
87//!                 size: 10,
88//!                 align_mask: 1,
89//!                 usage: UsageFlags::HOST_ACCESS,
90//!                 memory_types: !0,
91//!             },
92//!         )
93//!     }?;
94//!
95//!     unsafe {
96//!         block.write_bytes(
97//!             &device,
98//!             0,
99//!             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
100//!         )
101//!     }?;
102//!
103//!     unsafe { allocator.dealloc(&device, block) }
104//!
105//!     Ok(())
106//! }
107//! ```
108//!
109
110use 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            // Safe because `Self` is `repr(transparent)`
128            // with only field being `DeviceLoader`.
129            &*(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
265/// Returns `DeviceProperties` from erupt's `InstanceLoader` for specified `PhysicalDevice`, required to create `GpuAllocator`.
266///
267/// # Safety
268///
269/// `physical_device` must be queried from `Instance` associated with this `instance`.
270/// Even if returned properties' field `buffer_device_address` is set to true,
271/// feature `PhysicalDeviceBufferDeviceAddressFeatures::buffer_derive_address`  must be enabled explicitly on device creation
272/// and extension "VK_KHR_buffer_device_address" for Vulkan prior 1.2.
273/// Otherwise the field must be set to false before passing to `GpuAllocator::new`.
274pub 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(), // FIXME: Can query this information if instance is v1.1
332        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}