gpu_alloc_ash/
lib.rs

1//!
2//! # Ash backend for `gpu-alloc`
3//!  
4//! # Usage example
5//!
6//! ```ignore
7//! use {
8//!     ash::{vk, DefaultEntryLoader, DeviceLoader, InstanceLoader},
9//!     gpu_alloc::{Config, GpuAllocator, Request, UsageFlags},
10//!     gpu_alloc_ash::{device_properties, AshMemoryDevice},
11//!     std::ffi::CStr,
12//! };
13//!
14//! fn main() -> eyre::Result<()> {
15//!     color_eyre::install()?;
16//!
17//!     let entry = DefaultEntryLoader::new()?;
18//!
19//!     let instance = InstanceLoader::new(
20//!         &entry,
21//!         &vk::InstanceCreateInfo::default()
22//!             .into_builder()
23//!             .application_info(
24//!                 &vk::ApplicationInfo::default()
25//!                     .into_builder()
26//!                     .engine_name(CStr::from_bytes_with_nul(b"GpuAlloc\0").unwrap())
27//!                     .engine_version(1)
28//!                     .application_name(CStr::from_bytes_with_nul(b"GpuAllocApp\0").unwrap())
29//!                     .application_version(1)
30//!                     .api_version(entry.instance_version()),
31//!             ),
32//!         None,
33//!     )?;
34//!
35//!     let physical_devices = unsafe { instance.enumerate_physical_devices(None) }.result()?;
36//!     let physical_device = physical_devices[0];
37//!
38//!     let props = unsafe { device_properties(&instance, physical_device) }?;
39//!
40//!     let device = DeviceLoader::new(
41//!         &instance,
42//!         physical_device,
43//!         &vk::DeviceCreateInfoBuilder::new().queue_create_infos(&[
44//!             vk::DeviceQueueCreateInfoBuilder::new()
45//!                 .queue_family_index(0)
46//!                 .queue_priorities(&[0f32]),
47//!         ]),
48//!         None,
49//!     )?;
50//!
51//!     let config = Config::i_am_potato();
52//!
53//!     let mut allocator = GpuAllocator::new(config, props);
54//!
55//!     let mut block = unsafe {
56//!         allocator.alloc(
57//!             AshMemoryDevice::wrap(&device),
58//!             Request {
59//!                 size: 10,
60//!                 align_mask: 1,
61//!                 usage: UsageFlags::HOST_ACCESS,
62//!                 memory_types: !0,
63//!             },
64//!         )
65//!     }?;
66//!
67//!     unsafe {
68//!         block.write_bytes(
69//!             AshMemoryDevice::wrap(&device),
70//!             0,
71//!             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
72//!         )
73//!     }?;
74//!
75//!     unsafe { allocator.dealloc(AshMemoryDevice::wrap(&device), block) }
76//!
77//!     // the `ash::Device` also implements `AsRef<AshMemoryDevice>`
78//!     // you can pass a reference of `ash::Device` directly as argument
79//!     let mut block = unsafe {
80//!         allocator.alloc(
81//!             &device,
82//!             Request {
83//!                 size: 10,
84//!                 align_mask: 1,
85//!                 usage: UsageFlags::HOST_ACCESS,
86//!                 memory_types: !0,
87//!             },
88//!         )
89//!     }?;
90//!
91//!     unsafe {
92//!         block.write_bytes(
93//!             &device,
94//!             0,
95//!             &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
96//!         )
97//!     }?;
98//!
99//!     unsafe { allocator.dealloc(&device, block) }
100//!
101//!     Ok(())
102//! }
103//! ```
104//!
105
106use {
107    ash::{vk, Device, Instance},
108    gpu_alloc_types::{
109        AllocationFlags, DeviceMapError, DeviceProperties, MappedMemoryRange, MemoryDevice,
110        MemoryHeap, MemoryPropertyFlags, MemoryType, OutOfMemory,
111    },
112    std::ptr::NonNull,
113    tinyvec::TinyVec,
114};
115
116#[repr(transparent)]
117pub struct AshMemoryDevice {
118    device: Device,
119}
120
121impl AshMemoryDevice {
122    pub fn wrap(device: &Device) -> &Self {
123        unsafe {
124            // Safe because `Self` is `repr(transparent)`
125            // with only field being `DeviceLoader`.
126            &*(device as *const Device as *const Self)
127        }
128    }
129}
130
131impl AsRef<AshMemoryDevice> for Device {
132    #[inline(always)]
133    fn as_ref(&self) -> &AshMemoryDevice {
134        AshMemoryDevice::wrap(self)
135    }
136}
137
138// AsRef does not have a blanket implementation. need to add this impl so that
139// old user code (i.e. explicit wrap) still compiles without any change
140impl AsRef<AshMemoryDevice> for AshMemoryDevice {
141    #[inline(always)]
142    fn as_ref(&self) -> &AshMemoryDevice {
143        self
144    }
145}
146
147impl MemoryDevice<vk::DeviceMemory> for AshMemoryDevice {
148    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
149    unsafe fn allocate_memory(
150        &self,
151        size: u64,
152        memory_type: u32,
153        flags: AllocationFlags,
154    ) -> Result<vk::DeviceMemory, OutOfMemory> {
155        assert!((flags & !(AllocationFlags::DEVICE_ADDRESS)).is_empty());
156
157        let mut info = vk::MemoryAllocateInfo::default()
158            .allocation_size(size)
159            .memory_type_index(memory_type);
160
161        let mut info_flags;
162
163        if flags.contains(AllocationFlags::DEVICE_ADDRESS) {
164            info_flags = vk::MemoryAllocateFlagsInfo::default()
165                .flags(vk::MemoryAllocateFlags::DEVICE_ADDRESS);
166            info = info.push_next(&mut info_flags);
167        }
168
169        match self.device.allocate_memory(&info, None) {
170            Ok(memory) => Ok(memory),
171            Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(OutOfMemory::OutOfDeviceMemory),
172            Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(OutOfMemory::OutOfHostMemory),
173            Err(vk::Result::ERROR_TOO_MANY_OBJECTS) => panic!("Too many objects"),
174            Err(err) => panic!("Unexpected Vulkan error: `{}`", err),
175        }
176    }
177
178    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
179    unsafe fn deallocate_memory(&self, memory: vk::DeviceMemory) {
180        self.device.free_memory(memory, None);
181    }
182
183    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
184    unsafe fn map_memory(
185        &self,
186        memory: &mut vk::DeviceMemory,
187        offset: u64,
188        size: u64,
189    ) -> Result<NonNull<u8>, DeviceMapError> {
190        match self
191            .device
192            .map_memory(*memory, offset, size, vk::MemoryMapFlags::empty())
193        {
194            Ok(ptr) => {
195                Ok(NonNull::new(ptr as *mut u8)
196                    .expect("Pointer to memory mapping must not be null"))
197            }
198            Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => Err(DeviceMapError::OutOfDeviceMemory),
199            Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => Err(DeviceMapError::OutOfHostMemory),
200            Err(vk::Result::ERROR_MEMORY_MAP_FAILED) => Err(DeviceMapError::MapFailed),
201            Err(err) => panic!("Unexpected Vulkan error: `{}`", err),
202        }
203    }
204
205    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
206    unsafe fn unmap_memory(&self, memory: &mut vk::DeviceMemory) {
207        self.device.unmap_memory(*memory);
208    }
209
210    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
211    unsafe fn invalidate_memory_ranges(
212        &self,
213        ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>],
214    ) -> Result<(), OutOfMemory> {
215        self.device
216            .invalidate_mapped_memory_ranges(
217                &ranges
218                    .iter()
219                    .map(|range| {
220                        vk::MappedMemoryRange::default()
221                            .memory(*range.memory)
222                            .offset(range.offset)
223                            .size(range.size)
224                    })
225                    .collect::<TinyVec<[_; 4]>>(),
226            )
227            .map_err(|err| match err {
228                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
229                vk::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
230                err => panic!("Unexpected Vulkan error: `{}`", err),
231            })
232    }
233
234    #[cfg_attr(feature = "tracing", tracing::instrument(skip(self)))]
235    unsafe fn flush_memory_ranges(
236        &self,
237        ranges: &[MappedMemoryRange<'_, vk::DeviceMemory>],
238    ) -> Result<(), OutOfMemory> {
239        self.device
240            .flush_mapped_memory_ranges(
241                &ranges
242                    .iter()
243                    .map(|range| {
244                        vk::MappedMemoryRange::default()
245                            .memory(*range.memory)
246                            .offset(range.offset)
247                            .size(range.size)
248                    })
249                    .collect::<TinyVec<[_; 4]>>(),
250            )
251            .map_err(|err| match err {
252                vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => OutOfMemory::OutOfDeviceMemory,
253                vk::Result::ERROR_OUT_OF_HOST_MEMORY => OutOfMemory::OutOfHostMemory,
254                err => panic!("Unexpected Vulkan error: `{}`", err),
255            })
256    }
257}
258
259/// Returns `DeviceProperties` from ash's `InstanceLoader` for specified `PhysicalDevice`, required to create `GpuAllocator`.
260///
261/// # Safety
262///
263/// `physical_device` must be queried from `Instance` associated with this `instance`.
264/// Even if returned properties' field `buffer_device_address` is set to true,
265/// feature `PhysicalDeviceBufferDeviceAddressFeatures::buffer_derive_address`  must be enabled explicitly on device creation
266/// and extension "VK_KHR_buffer_device_address" for Vulkan prior 1.2.
267/// Otherwise the field must be set to false before passing to `GpuAllocator::new`.
268pub unsafe fn device_properties(
269    instance: &Instance,
270    version: u32,
271    physical_device: vk::PhysicalDevice,
272) -> Result<DeviceProperties<'static>, vk::Result> {
273    use ash::vk::PhysicalDeviceFeatures2;
274
275    let limits = instance
276        .get_physical_device_properties(physical_device)
277        .limits;
278
279    let memory_properties = instance.get_physical_device_memory_properties(physical_device);
280
281    let buffer_device_address =
282        if vk::api_version_major(version) >= 1 && vk::api_version_minor(version) >= 2 {
283            let mut features = PhysicalDeviceFeatures2::default();
284            let mut bda_features = vk::PhysicalDeviceBufferDeviceAddressFeatures::default();
285            features.p_next =
286                &mut bda_features as *mut vk::PhysicalDeviceBufferDeviceAddressFeatures as *mut _;
287            instance.get_physical_device_features2(physical_device, &mut features);
288            bda_features.buffer_device_address != 0
289        } else {
290            false
291        };
292
293    Ok(DeviceProperties {
294        max_memory_allocation_count: limits.max_memory_allocation_count,
295        max_memory_allocation_size: u64::max_value(), // FIXME: Can query this information if instance is v1.1
296        non_coherent_atom_size: limits.non_coherent_atom_size,
297        memory_types: memory_properties.memory_types
298            [..memory_properties.memory_type_count as usize]
299            .iter()
300            .map(|memory_type| MemoryType {
301                props: memory_properties_from_ash(memory_type.property_flags),
302                heap: memory_type.heap_index,
303            })
304            .collect(),
305        memory_heaps: memory_properties.memory_heaps
306            [..memory_properties.memory_heap_count as usize]
307            .iter()
308            .map(|&memory_heap| MemoryHeap {
309                size: memory_heap.size,
310            })
311            .collect(),
312        buffer_device_address,
313    })
314}
315
316pub fn memory_properties_from_ash(props: vk::MemoryPropertyFlags) -> MemoryPropertyFlags {
317    let mut result = MemoryPropertyFlags::empty();
318    if props.contains(vk::MemoryPropertyFlags::DEVICE_LOCAL) {
319        result |= MemoryPropertyFlags::DEVICE_LOCAL;
320    }
321    if props.contains(vk::MemoryPropertyFlags::HOST_VISIBLE) {
322        result |= MemoryPropertyFlags::HOST_VISIBLE;
323    }
324    if props.contains(vk::MemoryPropertyFlags::HOST_COHERENT) {
325        result |= MemoryPropertyFlags::HOST_COHERENT;
326    }
327    if props.contains(vk::MemoryPropertyFlags::HOST_CACHED) {
328        result |= MemoryPropertyFlags::HOST_CACHED;
329    }
330    if props.contains(vk::MemoryPropertyFlags::LAZILY_ALLOCATED) {
331        result |= MemoryPropertyFlags::LAZILY_ALLOCATED;
332    }
333    result
334}
335
336pub fn memory_properties_to_ash(props: MemoryPropertyFlags) -> vk::MemoryPropertyFlags {
337    let mut result = vk::MemoryPropertyFlags::empty();
338    if props.contains(MemoryPropertyFlags::DEVICE_LOCAL) {
339        result |= vk::MemoryPropertyFlags::DEVICE_LOCAL;
340    }
341    if props.contains(MemoryPropertyFlags::HOST_VISIBLE) {
342        result |= vk::MemoryPropertyFlags::HOST_VISIBLE;
343    }
344    if props.contains(MemoryPropertyFlags::HOST_COHERENT) {
345        result |= vk::MemoryPropertyFlags::HOST_COHERENT;
346    }
347    if props.contains(MemoryPropertyFlags::HOST_CACHED) {
348        result |= vk::MemoryPropertyFlags::HOST_CACHED;
349    }
350    if props.contains(MemoryPropertyFlags::LAZILY_ALLOCATED) {
351        result |= vk::MemoryPropertyFlags::LAZILY_ALLOCATED;
352    }
353    result
354}