vk_mem_vulkanalia/
lib.rs

1//! Easy to use, high performance memory manager for Vulkan.
2#![cfg_attr(not(feature = "std"), no_std)]
3#![cfg_attr(feature = "core_error", feature(no_std_error))]
4
5extern crate alloc;
6
7mod definitions;
8mod defragmentation;
9mod ffi;
10mod pool;
11mod utils;
12mod virtual_block;
13pub use definitions::*;
14pub use defragmentation::*;
15pub use pool::*;
16pub use utils::*;
17pub use virtual_block::*;
18
19use vulkanalia::VkResult;
20use vulkanalia::vk;
21use vulkanalia::vk::DeviceBufferMemoryRequirements;
22use vulkanalia::vk::MemoryRequirements2;
23use vulkanalia::vk::{
24    DeviceV1_0,
25    InstanceV1_0
26};
27use vulkanalia_sys::DeviceImageMemoryRequirements;
28use std::mem;
29
30/// Main allocator object
31pub struct Allocator {
32    /// Pointer to internal VmaAllocator instance
33    internal: ffi::VmaAllocator,
34}
35
36// Allocator is internally thread safe unless AllocatorCreateFlags::EXTERNALLY_SYNCHRONIZED is used (then you need to add synchronization!)
37unsafe impl Send for Allocator {}
38unsafe impl Sync for Allocator {}
39
40/// Represents single memory allocation.
41///
42/// It may be either dedicated block of `vk::DeviceMemory` or a specific region of a
43/// bigger block of this type plus unique offset.
44///
45/// Although the library provides convenience functions that create a Vulkan buffer or image,
46/// allocate memory for it and bind them together, binding of the allocation to a buffer or an
47/// image is out of scope of the allocation itself.
48///
49/// Allocation object can exist without buffer/image bound, binding can be done manually by
50/// the user, and destruction of it can be done independently of destruction of the allocation.
51///
52/// The object also remembers its size and some other information. To retrieve this information,
53/// use `Allocator::get_allocation_info`.
54///
55/// Some kinds allocations can be in lost state.
56#[derive(Debug)]
57pub struct Allocation(ffi::VmaAllocation);
58unsafe impl Send for Allocation {}
59unsafe impl Sync for Allocation {}
60
61impl Allocator {
62    /// Construct a new `Allocator` using the provided options.
63    /// Safety: [`AllocatorCreateInfo::instance`], [`AllocatorCreateInfo::device`] and
64    /// [`AllocatorCreateInfo::physical_device`] must be valid throughout the lifetime of the allocator.
65    pub unsafe fn new(create_info: AllocatorCreateInfo) -> VkResult<Self> {
66        unsafe extern "system" fn get_instance_proc_addr_stub(
67            _instance: vk::Instance,
68            _p_name: *const ::std::os::raw::c_char,
69        ) -> vk::PFN_vkVoidFunction {
70            panic!("VMA_DYNAMIC_VULKAN_FUNCTIONS is unsupported")
71        }
72
73        unsafe extern "system" fn get_get_device_proc_stub(
74            _device: vk::Device,
75            _p_name: *const ::std::os::raw::c_char,
76        ) -> vk::PFN_vkVoidFunction {
77            panic!("VMA_DYNAMIC_VULKAN_FUNCTIONS is unsupported")
78        }
79
80        let mut raw_create_info = ffi::VmaAllocatorCreateInfo {
81            flags: create_info.flags.bits(),
82            physicalDevice: create_info.physical_device,
83            device: create_info.device.handle(),
84            preferredLargeHeapBlockSize: create_info.preferred_large_heap_block_size,
85            pAllocationCallbacks: create_info
86                .allocation_callbacks
87                .map(|a| unsafe { std::mem::transmute(a) })
88                .unwrap_or(std::ptr::null()),
89            pDeviceMemoryCallbacks: create_info
90                .device_memory_callbacks
91                .map(|a| a as *const _)
92                .unwrap_or(std::ptr::null()),
93            pHeapSizeLimit: if create_info.heap_size_limits.is_empty() {
94                std::ptr::null()
95            } else {
96                create_info.heap_size_limits.as_ptr()
97            },
98            instance: create_info.instance.handle(),
99            vulkanApiVersion: create_info.vulkan_api_version,
100            pVulkanFunctions: std::ptr::null(),
101            pTypeExternalMemoryHandleTypes: if create_info
102                .type_external_memory_handle_types
103                .is_empty()
104            {
105                std::ptr::null()
106            } else {
107                create_info.type_external_memory_handle_types.as_ptr()
108            },
109        };
110
111        let version = create_info.instance.get_physical_device_properties(create_info.physical_device).api_version;
112        println!("vk version {}.{}", vk::version_major(version), vk::version_minor(version));
113
114        for extension in create_info.instance.extensions() {
115            println!("Extension {}", extension);
116        }
117
118        let has_ext = create_info.instance.extensions().contains(&vk::KHR_GET_PHYSICAL_DEVICE_PROPERTIES2_EXTENSION.name);
119
120        println!("Includes? {}", has_ext);
121
122        #[cfg(feature = "libloading")]
123        let routed_functions = ffi::VmaVulkanFunctions {
124            vkGetInstanceProcAddr: get_instance_proc_addr_stub,
125            vkGetDeviceProcAddr: get_get_device_proc_stub,
126            vkGetPhysicalDeviceProperties: create_info
127                .instance
128                .commands()
129                .get_physical_device_properties,
130            vkGetPhysicalDeviceMemoryProperties: create_info
131                .instance
132                .commands()
133                .get_physical_device_memory_properties,
134            vkAllocateMemory: create_info.device.commands().allocate_memory,
135            vkFreeMemory: create_info.device.commands().free_memory,
136            vkMapMemory: create_info.device.commands().map_memory,
137            vkUnmapMemory: create_info.device.commands().unmap_memory,
138            vkFlushMappedMemoryRanges: create_info.device.commands().flush_mapped_memory_ranges,
139            vkInvalidateMappedMemoryRanges: create_info
140                .device
141                .commands()
142                .invalidate_mapped_memory_ranges,
143            vkBindBufferMemory: create_info.device.commands().bind_buffer_memory,
144            vkBindImageMemory: create_info.device.commands().bind_image_memory,
145            vkGetBufferMemoryRequirements: create_info
146                .device
147                .commands()
148                .get_buffer_memory_requirements,
149            vkGetImageMemoryRequirements: create_info
150                .device
151                .commands()
152                .get_image_memory_requirements,
153            vkCreateBuffer: create_info.device.commands().create_buffer,
154            vkDestroyBuffer: create_info.device.commands().destroy_buffer,
155            vkCreateImage: create_info.device.commands().create_image,
156            vkDestroyImage: create_info.device.commands().destroy_image,
157            vkCmdCopyBuffer: create_info.device.commands().cmd_copy_buffer,
158            vkGetBufferMemoryRequirements2KHR: create_info
159                .device
160                .commands()
161                .get_buffer_memory_requirements2_khr,
162            vkGetImageMemoryRequirements2KHR: create_info
163                .device
164                .commands()
165                .get_image_memory_requirements2_khr,
166            vkBindBufferMemory2KHR: create_info.device.commands().bind_buffer_memory2_khr,
167            vkBindImageMemory2KHR: create_info.device.commands().bind_image_memory2_khr,
168            vkGetPhysicalDeviceMemoryProperties2KHR: create_info
169                .instance
170                .commands()
171                .get_physical_device_memory_properties2_khr,
172            vkGetDeviceBufferMemoryRequirements: pick_get_device_buffer_memory_requirements_fn(
173                &create_info),
174            vkGetDeviceImageMemoryRequirements: pick_get_device_image_memory_requirements_fn(&create_info),
175        };
176        #[cfg(feature = "libloading")]
177        {
178            raw_create_info.pVulkanFunctions = &routed_functions;
179        }
180        unsafe {
181            let mut internal: ffi::VmaAllocator = mem::zeroed();
182            ffi::vmaCreateAllocator(&raw_create_info, &mut internal);
183
184            Ok(Allocator { internal })
185        }
186    }
187
188    /// The allocator fetches `vk::PhysicalDeviceProperties` from the physical device.
189    /// You can get it here, without fetching it again on your own.
190    pub unsafe fn get_physical_device_properties(&self) -> VkResult<vk::PhysicalDeviceProperties> {
191        let mut properties = vk::PhysicalDeviceProperties::default();
192        ffi::vmaGetPhysicalDeviceProperties(
193            self.internal,
194            &mut properties as *mut _ as *mut *const _,
195        );
196
197        Ok(properties)
198    }
199
200    /// The allocator fetches `vk::PhysicalDeviceMemoryProperties` from the physical device.
201    /// You can get it here, without fetching it again on your own.
202    pub unsafe fn get_memory_properties(&self) -> &vk::PhysicalDeviceMemoryProperties {
203        let mut properties: *const vk::PhysicalDeviceMemoryProperties = std::ptr::null();
204        ffi::vmaGetMemoryProperties(self.internal, &mut properties);
205
206        &*properties
207    }
208
209    /// Sets index of the current frame.
210    ///
211    /// This function must be used if you make allocations with `AllocationCreateFlags::CAN_BECOME_LOST` and
212    /// `AllocationCreateFlags::CAN_MAKE_OTHER_LOST` flags to inform the allocator when a new frame begins.
213    /// Allocations queried using `Allocator::get_allocation_info` cannot become lost
214    /// in the current frame.
215    pub unsafe fn set_current_frame_index(&self, frame_index: u32) {
216        ffi::vmaSetCurrentFrameIndex(self.internal, frame_index);
217    }
218
219    /// Retrieves statistics from current state of the `Allocator`.
220    pub fn calculate_statistics(&self) -> VkResult<ffi::VmaTotalStatistics> {
221        unsafe {
222            let mut vma_stats: ffi::VmaTotalStatistics = mem::zeroed();
223            ffi::vmaCalculateStatistics(self.internal, &mut vma_stats);
224            Ok(vma_stats)
225        }
226    }
227
228    /// Retrieves information about current memory usage and budget for all memory heaps.
229    ///
230    /// This function is called "get" not "calculate" because it is very fast, suitable to be called
231    /// every frame or every allocation. For more detailed statistics use vmaCalculateStatistics().
232    ///
233    /// Note that when using allocator from multiple threads, returned information may immediately
234    /// become outdated.
235    pub fn get_heap_budgets(&self) -> VkResult<Vec<ffi::VmaBudget>> {
236        unsafe {
237            let len = self.get_memory_properties().memory_heap_count as usize;
238            let mut vma_budgets: Vec<ffi::VmaBudget> = Vec::with_capacity(len);
239            ffi::vmaGetHeapBudgets(self.internal, vma_budgets.as_mut_ptr());
240            vma_budgets.set_len(len);
241            Ok(vma_budgets)
242        }
243    }
244
245    /// Frees memory previously allocated using `Allocator::allocate_memory`,
246    /// `Allocator::allocate_memory_for_buffer`, or `Allocator::allocate_memory_for_image`.
247    pub unsafe fn free_memory(&self, allocation: &mut Allocation) {
248        ffi::vmaFreeMemory(self.internal, allocation.0);
249    }
250
251    /// Frees memory and destroys multiple allocations.
252    ///
253    /// Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
254    /// It is just a general purpose function to free memory and destroy allocations made using e.g. `Allocator::allocate_memory',
255    /// 'Allocator::allocate_memory_pages` and other functions.
256    ///
257    /// It may be internally optimized to be more efficient than calling 'Allocator::free_memory` `allocations.len()` times.
258    ///
259    /// Allocations in 'allocations' slice can come from any memory pools and types.
260    pub unsafe fn free_memory_pages(&self, allocations: &mut [Allocation]) {
261        ffi::vmaFreeMemoryPages(
262            self.internal,
263            allocations.len(),
264            allocations.as_ptr() as *mut _,
265        );
266    }
267
268    /// Returns current information about specified allocation and atomically marks it as used in current frame.
269    ///
270    /// Current parameters of given allocation are returned in the result object, available through accessors.
271    ///
272    /// This function also atomically "touches" allocation - marks it as used in current frame,
273    /// just like `Allocator::touch_allocation`.
274    ///
275    /// If the allocation is in lost state, `allocation.get_device_memory` returns `vk::DeviceMemory::null()`.
276    ///
277    /// Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
278    /// you can avoid calling it too often.
279    ///
280    /// If you just want to check if allocation is not lost, `Allocator::touch_allocation` will work faster.
281    pub fn get_allocation_info(&self, allocation: &Allocation) -> AllocationInfo {
282        unsafe {
283            let mut allocation_info: ffi::VmaAllocationInfo = mem::zeroed();
284            ffi::vmaGetAllocationInfo(self.internal, allocation.0, &mut allocation_info);
285            allocation_info.into()
286        }
287    }
288
289    /// Sets user data in given allocation to new value.
290    ///
291    /// If the allocation was created with `AllocationCreateFlags::USER_DATA_COPY_STRING`,
292    /// `user_data` must be either null, or pointer to a null-terminated string. The function
293    /// makes local copy of the string and sets it as allocation's user data. String
294    /// passed as user data doesn't need to be valid for whole lifetime of the allocation -
295    /// you can free it after this call. String previously pointed by allocation's
296    /// user data is freed from memory.
297    ///
298    /// If the flag was not used, the value of pointer `user_data` is just copied to
299    /// allocation's user data. It is opaque, so you can use it however you want - e.g.
300    /// as a pointer, ordinal number or some handle to you own data.
301    pub unsafe fn set_allocation_user_data(
302        &self,
303        allocation: &mut Allocation,
304        user_data: *mut ::std::os::raw::c_void,
305    ) {
306        ffi::vmaSetAllocationUserData(self.internal, allocation.0, user_data);
307    }
308
309    /// Maps memory represented by given allocation and returns pointer to it.
310    ///
311    /// Maps memory represented by given allocation to make it accessible to CPU code.
312    /// When succeeded, result is a pointer to first byte of this memory.
313    ///
314    /// If the allocation is part of bigger `vk::DeviceMemory` block, the pointer is
315    /// correctly offseted to the beginning of region assigned to this particular
316    /// allocation.
317    ///
318    /// Mapping is internally reference-counted and synchronized, so despite raw Vulkan
319    /// function `vk::Device::MapMemory` cannot be used to map same block of
320    /// `vk::DeviceMemory` multiple times simultaneously, it is safe to call this
321    /// function on allocations assigned to the same memory block. Actual Vulkan memory
322    /// will be mapped on first mapping and unmapped on last unmapping.
323    ///
324    /// If the function succeeded, you must call `Allocator::unmap_memory` to unmap the
325    /// allocation when mapping is no longer needed or before freeing the allocation, at
326    /// the latest.
327    ///
328    /// It also safe to call this function multiple times on the same allocation. You
329    /// must call `Allocator::unmap_memory` same number of times as you called
330    /// `Allocator::map_memory`.
331    ///
332    /// It is also safe to call this function on allocation created with
333    /// `AllocationCreateFlags::MAPPED` flag. Its memory stays mapped all the time.
334    /// You must still call `Allocator::unmap_memory` same number of times as you called
335    /// `Allocator::map_memory`. You must not call `Allocator::unmap_memory` additional
336    /// time to free the "0-th" mapping made automatically due to `AllocationCreateFlags::MAPPED` flag.
337    ///
338    /// This function fails when used on allocation made in memory type that is not
339    /// `vk::MemoryPropertyFlags::HOST_VISIBLE`.
340    ///
341    /// This function always fails when called for allocation that was created with
342    /// `AllocationCreateFlags::CAN_BECOME_LOST` flag. Such allocations cannot be mapped.
343    pub unsafe fn map_memory(&self, allocation: &mut Allocation) -> VkResult<*mut u8> {
344        let mut mapped_data: *mut ::std::os::raw::c_void = ::std::ptr::null_mut();
345        ffi::vmaMapMemory(self.internal, allocation.0, &mut mapped_data);
346
347        Ok(mapped_data as *mut u8)
348    }
349
350    /// Unmaps memory represented by given allocation, mapped previously using `Allocator::map_memory`.
351    pub unsafe fn unmap_memory(&self, allocation: &mut Allocation) {
352        ffi::vmaUnmapMemory(self.internal, allocation.0);
353    }
354
355    /// Flushes memory of given allocation.
356    ///
357    /// Calls `vk::Device::FlushMappedMemoryRanges` for memory associated with given range of given allocation.
358    ///
359    /// - `offset` must be relative to the beginning of allocation.
360    /// - `size` can be `vk::WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
361    /// - `offset` and `size` don't have to be aligned; hey are internally rounded down/up to multiple of `nonCoherentAtomSize`.
362    /// - If `size` is 0, this call is ignored.
363    /// - If memory type that the `allocation` belongs to is not `vk::MemoryPropertyFlags::HOST_VISIBLE` or it is `vk::MemoryPropertyFlags::HOST_COHERENT`, this call is ignored.
364    pub fn flush_allocation(
365        &self,
366        allocation: &Allocation,
367        offset: vk::DeviceSize,
368        size: vk::DeviceSize,
369    ) -> VkResult<()> {
370        to_vk_result(unsafe { ffi::vmaFlushAllocation(self.internal, allocation.0, offset, size) })
371    }
372
373    /// Invalidates memory of given allocation.
374    ///
375    /// Calls `vk::Device::invalidate_mapped_memory_ranges` for memory associated with given range of given allocation.
376    ///
377    /// - `offset` must be relative to the beginning of allocation.
378    /// - `size` can be `vk::WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
379    /// - `offset` and `size` don't have to be aligned. They are internally rounded down/up to multiple of `nonCoherentAtomSize`.
380    /// - If `size` is 0, this call is ignored.
381    /// - If memory type that the `allocation` belongs to is not `vk::MemoryPropertyFlags::HOST_VISIBLE` or it is `vk::MemoryPropertyFlags::HOST_COHERENT`, this call is ignored.
382    pub fn invalidate_allocation(
383        &self,
384        allocation: &Allocation,
385        offset: vk::DeviceSize,
386        size: vk::DeviceSize,
387    ) -> VkResult<()> {
388        to_vk_result(unsafe { ffi::vmaInvalidateAllocation(self.internal, allocation.0, offset, size) })
389    }
390
391    /// Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
392    ///
393    /// `memory_type_bits` bit mask, where each bit set means that a memory type with that index should be checked.
394    ///
395    /// Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
396    /// `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are `HOST_VISIBLE` and `HOST_COHERENT`.
397    ///
398    /// Possible error values:
399    ///
400    /// - `vk::Result::ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
401    /// - `vk::Result::ERROR_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
402    ///   `VMA_ASSERT` is also fired in that case.
403    /// - Other value: Error returned by Vulkan, e.g. memory mapping failure.
404    pub unsafe fn check_corruption(&self, memory_types: vk::MemoryPropertyFlags) -> VkResult<()> {
405        to_vk_result(ffi::vmaCheckCorruption(self.internal, memory_types.bits()))
406    }
407
408    /// Binds buffer to allocation.
409    ///
410    /// Binds specified buffer to region of memory represented by specified allocation.
411    /// Gets `vk::DeviceMemory` handle and offset from the allocation.
412    ///
413    /// If you want to create a buffer, allocate memory for it and bind them together separately,
414    /// you should use this function for binding instead of `vk::Device::bind_buffer_memory`,
415    /// because it ensures proper synchronization so that when a `vk::DeviceMemory` object is
416    /// used by multiple allocations, calls to `vk::Device::bind_buffer_memory()` or
417    /// `vk::Device::map_memory()` won't happen from multiple threads simultaneously
418    /// (which is illegal in Vulkan).
419    ///
420    /// It is recommended to use function `Allocator::create_buffer` instead of this one.
421    pub unsafe fn bind_buffer_memory(
422        &self,
423        allocation: &Allocation,
424        buffer: vk::Buffer,
425    ) -> VkResult<()> {
426        to_vk_result(ffi::vmaBindBufferMemory(self.internal, allocation.0, buffer))
427    }
428
429    /// Binds buffer to allocation with additional parameters.
430    ///
431    /// * `allocation`
432    /// * `allocation_local_offset` - Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
433    /// * `buffer`
434    /// * `next` - A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
435    ///
436    /// This function is similar to vmaBindImageMemory(), but it provides additional parameters.
437    ///
438    /// If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
439    /// or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
440    pub unsafe fn bind_buffer_memory2(
441        &self,
442        allocation: &Allocation,
443        allocation_local_offset: vk::DeviceSize,
444        buffer: vk::Buffer,
445        next: *const ::std::os::raw::c_void,
446    ) -> VkResult<()> {
447        to_vk_result(ffi::vmaBindBufferMemory2(
448            self.internal,
449            allocation.0,
450            allocation_local_offset,
451            buffer,
452            next,
453        ))
454    }
455
456    /// Binds image to allocation.
457    ///
458    /// Binds specified image to region of memory represented by specified allocation.
459    /// Gets `vk::DeviceMemory` handle and offset from the allocation.
460    ///
461    /// If you want to create a image, allocate memory for it and bind them together separately,
462    /// you should use this function for binding instead of `vk::Device::bind_image_memory`,
463    /// because it ensures proper synchronization so that when a `vk::DeviceMemory` object is
464    /// used by multiple allocations, calls to `vk::Device::bind_image_memory()` or
465    /// `vk::Device::map_memory()` won't happen from multiple threads simultaneously
466    /// (which is illegal in Vulkan).
467    ///
468    /// It is recommended to use function `Allocator::create_image` instead of this one.
469    pub unsafe fn bind_image_memory(
470        &self,
471        allocation: &Allocation,
472        image: vk::Image,
473    ) -> VkResult<()> {
474        to_vk_result(ffi::vmaBindImageMemory(self.internal, allocation.0, image))
475    }
476
477    /// Binds image to allocation with additional parameters.
478    ///
479    /// * `allocation`
480    /// * `allocation_local_offset` - Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.
481    /// * `image`
482    /// * `next` - A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.
483    ///
484    /// This function is similar to vmaBindImageMemory(), but it provides additional parameters.
485    ///
486    /// If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
487    /// or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.
488    pub unsafe fn bind_image_memory2(
489        &self,
490        allocation: &Allocation,
491        allocation_local_offset: vk::DeviceSize,
492        image: vk::Image,
493        next: *const ::std::os::raw::c_void,
494    ) -> VkResult<()> {
495        to_vk_result(ffi::vmaBindImageMemory2(
496            self.internal,
497            allocation.0,
498            allocation_local_offset,
499            image,
500            next,
501        ))
502    }
503
504    /// Destroys Vulkan buffer and frees allocated memory.
505    ///
506    /// This is just a convenience function equivalent to:
507    ///
508    /// ```ignore
509    /// vk::Device::destroy_buffer(buffer, None);
510    /// Allocator::free_memory(allocator, allocation);
511    /// ```
512    ///
513    /// It it safe to pass null as `buffer` and/or `allocation`.
514    pub unsafe fn destroy_buffer(&self, buffer: vk::Buffer, allocation: &mut Allocation) {
515        ffi::vmaDestroyBuffer(self.internal, buffer, allocation.0);
516    }
517
518    /// Destroys Vulkan image and frees allocated memory.
519    ///
520    /// This is just a convenience function equivalent to:
521    ///
522    /// ```ignore
523    /// vk::Device::destroy_image(image, None);
524    /// Allocator::free_memory(allocator, allocation);
525    /// ```
526    ///
527    /// It it safe to pass null as `image` and/or `allocation`.
528    pub unsafe fn destroy_image(&self, image: vk::Image, allocation: &mut Allocation) {
529        ffi::vmaDestroyImage(self.internal, image, allocation.0);
530    }
531    /// Flushes memory of given set of allocations."]
532    ///
533    /// Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations."]
534    /// For more information, see documentation of vmaFlushAllocation()."]
535    ///
536    /// * `allocations`
537    /// * `offsets` - If not None, it must be a slice of offsets of regions to flush, relative to the beginning of respective allocations. None means all ofsets are zero.
538    /// * `sizes` - If not None, it must be a slice of sizes of regions to flush in respective allocations. None means `VK_WHOLE_SIZE` for all allocations.
539    pub unsafe fn flush_allocations<'a>(
540        &self,
541        allocations: impl IntoIterator<Item = &'a Allocation>,
542        offsets: Option<&[vk::DeviceSize]>,
543        sizes: Option<&[vk::DeviceSize]>,
544    ) -> VkResult<()> {
545        let allocations: Vec<ffi::VmaAllocation> = allocations.into_iter().map(|a| a.0).collect();
546        to_vk_result(ffi::vmaFlushAllocations(
547            self.internal,
548            allocations.len() as u32,
549            allocations.as_ptr() as *mut _,
550            offsets.map_or(std::ptr::null(), |offsets| offsets.as_ptr()),
551            sizes.map_or(std::ptr::null(), |sizes| sizes.as_ptr()),
552        ))
553    }
554
555    /// Invalidates memory of given set of allocations."]
556    ///
557    /// Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations."]
558    /// For more information, see documentation of vmaInvalidateAllocation()."]
559    ///
560    /// * `allocations`
561    /// * `offsets` - If not None, it must be a slice of offsets of regions to flush, relative to the beginning of respective allocations. None means all ofsets are zero.
562    /// * `sizes` - If not None, it must be a slice of sizes of regions to flush in respective allocations. None means `VK_WHOLE_SIZE` for all allocations.
563    pub unsafe fn invalidate_allocations<'a>(
564        &self,
565        allocations: impl IntoIterator<Item = &'a Allocation>,
566        offsets: Option<&[vk::DeviceSize]>,
567        sizes: Option<&[vk::DeviceSize]>,
568    ) -> VkResult<()> {
569        let allocations: Vec<ffi::VmaAllocation> = allocations.into_iter().map(|a| a.0).collect();
570        to_vk_result(ffi::vmaInvalidateAllocations(
571            self.internal,
572            allocations.len() as u32,
573            allocations.as_ptr() as *mut _,
574            offsets.map_or(std::ptr::null(), |offsets| offsets.as_ptr()),
575            sizes.map_or(std::ptr::null(), |sizes| sizes.as_ptr()),
576        ))
577    }
578}
579
580/// Custom `Drop` implementation to clean up internal allocation instance
581impl Drop for Allocator {
582    fn drop(&mut self) {
583        unsafe {
584            ffi::vmaDestroyAllocator(self.internal);
585            self.internal = std::ptr::null_mut();
586        }
587    }
588}
589
590unsafe fn pick_get_device_buffer_memory_requirements_fn(
591    create_info: &AllocatorCreateInfo,
592) -> Option<
593    unsafe extern "system" fn(
594        vulkanalia_sys::Device,
595        *const DeviceBufferMemoryRequirements,
596        *mut MemoryRequirements2,
597    )
598> {
599    let version = create_info
600        .instance
601        .get_physical_device_properties(create_info.physical_device)
602        .api_version;
603
604    let major = vk::version_major(version);
605    let minor = vk::version_minor(version);
606
607    if major != 1 {
608        return None;
609    }
610
611    let commands = create_info.device.commands();
612
613    if minor == 3 {
614        return Some(commands.get_device_buffer_memory_requirements);
615    } else if minor >= 1 {
616        if create_info
617            .device
618            .extensions()
619            .contains(&vk::KHR_MAINTENANCE4_EXTENSION.name)
620        {
621            return Some(commands.get_device_buffer_memory_requirements_khr);
622        } else {
623            return None;
624        }
625    }
626
627    return None;
628}
629
630unsafe fn pick_get_device_image_memory_requirements_fn(
631    create_info: &AllocatorCreateInfo,
632) -> Option<
633    unsafe extern "system" fn(
634        vulkanalia_sys::Device,
635        *const DeviceImageMemoryRequirements,
636        *mut MemoryRequirements2,
637    )
638> {
639    let version = create_info
640        .instance
641        .get_physical_device_properties(create_info.physical_device)
642        .api_version;
643
644    let major = vk::version_major(version);
645    let minor = vk::version_minor(version);
646
647    if major != 1 {
648        return None;
649    }
650
651    let commands = create_info.device.commands();
652
653    if minor == 3 {
654        return Some(commands.get_device_image_memory_requirements);
655    } else if minor >= 1 {
656        if create_info
657            .device
658            .extensions()
659            .contains(&vk::KHR_MAINTENANCE4_EXTENSION.name)
660        {
661            return Some(commands.get_device_image_memory_requirements_khr);
662        } else {
663            return None;
664        }
665    }
666
667    return None;
668}