// This API and many of the comments in this file come
// directly from the VulkanMemoryAllocator source, which is
// released under the following license:
//
// Copyright (c) 2017-2019 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
const vk = @import("vulkan");
const vma_config = @import("vma_config.zig");
pub const config = if (builtin.mode == .Debug) vma_config.debugConfig else vma_config.releaseConfig;

const vulkan_call_conv = vk.vulkan_call_conv;

// callbacks use vulkan_call_conv, but the vma functions may not.
pub const CallConv = .C;

/// \struct Allocator
/// \brief Represents main object of this library initialized.
///
/// Fill structure #AllocatorCreateInfo and call function create() to create it.
/// Call function destroy() to destroy it.
///
/// It is recommended to create just one object of this type per `Device` object,
/// right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.
pub const Allocator = enum(usize) {
    Null = 0,
    _,

    /// Creates Allocator object.
    pub fn create(createInfo: AllocatorCreateInfo) !Allocator {
        var result: Allocator = undefined;
        const rc = vmaCreateAllocator(&createInfo, &result);
        if (@intFromEnum(rc) >= 0) return result;

        return error.VMACreateFailed;
    }

    /// Destroys allocator object.
    /// fn (Allocator) void
    pub const destroy = vmaDestroyAllocator;

    /// PhysicalDeviceProperties are fetched from physicalDevice by the allocator.
    /// You can access it here, without fetching it again on your own.
    pub fn getPhysicalDeviceProperties(allocator: Allocator) *const vk.PhysicalDeviceProperties {
        var properties: *const vk.PhysicalDeviceProperties = undefined;
        vmaGetPhysicalDeviceProperties(allocator, &properties);
        return properties;
    }

    /// PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.
    /// You can access it here, without fetching it again on your own.
    pub fn getMemoryProperties(allocator: Allocator) *const vk.PhysicalDeviceMemoryProperties {
        var properties: *const vk.PhysicalDeviceMemoryProperties = undefined;
        vmaGetMemoryProperties(allocator, &properties);
        return properties;
    }

    /// \brief Given Memory Type Index, returns Property Flags of this memory type.
    ///
    /// This is just a convenience function. Same information can be obtained using
    /// GetMemoryProperties().
    pub fn getMemoryTypeProperties(allocator: Allocator, memoryTypeIndex: u32) vk.MemoryPropertyFlags {
        var flags: vk.MemoryPropertyFlags align(4) = undefined;
        vmaGetMemoryTypeProperties(allocator, memoryTypeIndex, &flags);
        return flags;
    }

    /// \brief Sets index of the current frame.
    ///
    /// This function must be used if you make allocations with
    /// #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT and
    /// #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flags to inform the allocator
    /// when a new frame begins. Allocations queried using GetAllocationInfo() cannot
    /// become lost in the current frame.
    /// fn setCurrentFrameIndex(self: Allocator, frameIndex: u32) void
    pub const setCurrentFrameIndex = vmaSetCurrentFrameIndex;

    /// \brief Retrieves statistics from current state of the Allocator.
    ///
    /// This function is called "calculate" not "get" because it has to traverse all
    /// internal data structures, so it may be quite slow. For faster but more brief statistics
    /// suitable to be called every frame or every allocation, use GetBudget().
    ///
    /// Note that when using allocator from multiple threads, returned information may immediately
    /// become outdated.
    pub fn calculateStats(allocator: Allocator) Stats {
        var stats: Stats = undefined;
        vmaCalculateStats(allocator, &stats);
        return stats;
    }

    /// \brief Retrieves information about current memory budget for all memory heaps.
    ///
    /// \param[out] pBudget Must point to array with number of elements at least equal to number of memory heaps in physical device used.
    ///
    /// This function is called "get" not "calculate" because it is very fast, suitable to be called
    /// every frame or every allocation. For more detailed statistics use CalculateStats().
    ///
    /// Note that when using allocator from multiple threads, returned information may immediately
    /// become outdated.
    pub fn getBudget(allocator: Allocator) Budget {
        var budget: Budget = undefined;
        vmaGetBudget(allocator, &budget);
        return budget;
    }

    // pub usingnamespace if (config.statsStringEnabled)
    //     struct {
    //         /// Builds and returns statistics as string in JSON format.
    //         /// @param[out] ppStatsString Must be freed using FreeStatsString() function.
    //         pub fn buildStatsString(allocator: Allocator, detailedMap: bool) [*:0]u8 {
    //             var string: [*:0]u8 = undefined;
    //             vmaBuildStatsString(allocator, &string, @intFromBool(detailedMap));
    //             return string;
    //         }

    //         pub const freeStatsString = vmaFreeStatsString;
    //     }
    // else
    //     struct {};

    /// \brief Helps to find memoryTypeIndex, given memoryTypeBits and AllocationCreateInfo.
    ///
    /// This algorithm tries to find a memory type that:
    ///
    /// - Is allowed by memoryTypeBits.
    /// - Contains all the flags from pAllocationCreateInfo->requiredFlags.
    /// - Matches intended usage.
    /// - Has as many flags from pAllocationCreateInfo->preferredFlags as possible.
    ///
    /// \return Returns error.VK_FEATURE_NOT_PRESENT if not found. Receiving such result
    /// from this function or any other allocating function probably means that your
    /// device doesn't support any memory type with requested features for the specific
    /// type of resource you want to use it for. Please check parameters of your
    /// resource, like image layout (OPTIMAL versus LINEAR) or mip level count.
    pub fn findMemoryTypeIndex(allocator: Allocator, memoryTypeBits: u32, allocationCreateInfo: AllocationCreateInfo) !u32 {
        var index: u32 = undefined;
        const rc = vmaFindMemoryTypeIndex(allocator, memoryTypeBits, &allocationCreateInfo, &index);
        if (@intFromEnum(rc) >= 0) return index;

        if (rc == .error_feature_not_present) return error.VK_FEATURE_NOT_PRESENT;
        return error.VK_UNDOCUMENTED_ERROR;
    }

    /// \brief Helps to find memoryTypeIndex, given vk.BufferCreateInfo and AllocationCreateInfo.
    ///
    /// It can be useful e.g. to determine value to be used as PoolCreateInfo::memoryTypeIndex.
    /// It internally creates a temporary, dummy buffer that never has memory bound.
    /// It is just a convenience function, equivalent to calling:
    ///
    /// - `vkCreateBuffer`
    /// - `vkGetBufferMemoryRequirements`
    /// - `FindMemoryTypeIndex`
    /// - `vkDestroyBuffer`
    pub fn findMemoryTypeIndexForBufferInfo(
        allocator: Allocator,
        bufferCreateInfo: vk.BufferCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
    ) !u32 {
        var index: u32 = undefined;
        const rc = vmaFindMemoryTypeIndexForBufferInfo(allocator, &bufferCreateInfo, &allocationCreateInfo, &index);
        if (@intFromEnum(rc) >= 0) return index;

        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_feature_not_present => error.VK_FEATURE_NOT_PRESENT,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Helps to find memoryTypeIndex, given vk.ImageCreateInfo and AllocationCreateInfo.
    ///
    /// It can be useful e.g. to determine value to be used as PoolCreateInfo::memoryTypeIndex.
    /// It internally creates a temporary, dummy image that never has memory bound.
    /// It is just a convenience function, equivalent to calling:
    ///
    /// - `vkCreateImage`
    /// - `vkGetImageMemoryRequirements`
    /// - `FindMemoryTypeIndex`
    /// - `vkDestroyImage`
    pub fn findMemoryTypeIndexForImageInfo(
        allocator: Allocator,
        imageCreateInfo: vk.ImageCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
    ) !u32 {
        var index: u32 = undefined;
        const rc = vmaFindMemoryTypeIndexForImageInfo(allocator, &imageCreateInfo, &allocationCreateInfo, &index);
        if (@intFromEnum(rc) >= 0) return index;

        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_feature_not_present => error.VK_FEATURE_NOT_PRESENT,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Allocates Vulkan device memory and creates #Pool object.
    ///
    /// @param allocator Allocator object.
    /// @param pCreateInfo Parameters of pool to create.
    /// @param[out] pPool Handle to created pool.
    pub fn createPool(allocator: Allocator, createInfo: PoolCreateInfo) !Pool {
        var pool: Pool = undefined;
        const rc = vmaCreatePool(allocator, &createInfo, &pool);
        if (@intFromEnum(rc) >= 0) return pool;

        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Destroys #Pool object and frees Vulkan device memory.
    /// fn destroyPool(self: Allocator, pool: Pool) void
    pub const destroyPool = vmaDestroyPool;

    /// \brief Retrieves statistics of existing #Pool object.
    ///
    /// @param allocator Allocator object.
    /// @param pool Pool object.
    /// @param[out] pPoolStats Statistics of specified pool.
    pub fn getPoolStats(allocator: Allocator, pool: Pool) PoolStats {
        var stats: PoolStats = undefined;
        vmaGetPoolStats(allocator, pool, &stats);
        return stats;
    }

    /// \brief Marks all allocations in given pool as lost if they are not used in current frame or PoolCreateInfo::frameInUseCount back from now.
    ///
    /// @param allocator Allocator object.
    /// @param pool Pool.
    pub fn makePoolAllocationsLost(allocator: Allocator, pool: Pool) void {
        vmaMakePoolAllocationsLost(allocator, pool, null);
    }
    /// \brief Marks all allocations in given pool as lost if they are not used in current frame or PoolCreateInfo::frameInUseCount back from now.
    ///
    /// @param allocator Allocator object.
    /// @param pool Pool.
    /// @return the number of allocations that were marked as lost.
    pub fn makePoolAllocationsLostAndCount(allocator: Allocator, pool: Pool) usize {
        var count: usize = undefined;
        vmaMakePoolAllocationsLost(allocator, pool, &count);
        return count;
    }

    /// \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions.
    ///
    /// Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
    /// `VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is
    /// `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
    ///
    /// Possible return values:
    ///
    /// - `error.VK_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.
    /// - `vk.SUCCESS` - corruption detection has been performed and succeeded.
    /// - `error.VK_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
    /// `VMA_ASSERT` is also fired in that case.
    /// - Other value: Error returned by Vulkan, e.g. memory mapping failure.
    pub fn checkPoolCorruption(allocator: Allocator, pool: Pool) !void {
        const rc = vmaCheckPoolCorruption(allocator, pool);
        if (@intFromEnum(rc) >= 0) return;

        return switch (rc) {
            .error_feature_not_present => error.VMA_CORRUPTION_DETECTION_DISABLED,
            .error_validation_failed_ext => error.VMA_CORRUPTION_DETECTED,
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Retrieves name of a custom pool.
    ///
    /// After the call `ppName` is either null or points to an internally-owned null-terminated string
    /// containing name of the pool that was previously set. The pointer becomes invalid when the pool is
    /// destroyed or its name is changed using SetPoolName().
    pub fn getPoolName(allocator: Allocator, pool: Pool) ?[*:0]const u8 {
        var name: ?[*:0]const u8 = undefined;
        vmaGetPoolName(allocator, pool, &name);
        return name;
    }

    /// \brief Sets name of a custom pool.
    ///
    /// `pName` can be either null or pointer to a null-terminated string with new name for the pool.
    /// Function makes internal copy of the string, so it can be changed or freed immediately after this call.
    /// fn setPoolName(self: Allocator, pool: Pool, name: ?[*:0]const u8)
    pub const setPoolName = vmaSetPoolName;

    /// \brief General purpose memory allocation.
    ///
    /// @param[out] pAllocation Handle to allocated memory.
    /// @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function GetAllocationInfo().
    ///
    /// You should free the memory using FreeMemory() or FreeMemoryPages().
    ///
    /// It is recommended to use AllocateMemoryForBuffer(), AllocateMemoryForImage(),
    /// CreateBuffer(), CreateImage() instead whenever possible.
    pub fn allocateMemory(allocator: Allocator, vkMemoryRequirements: vk.MemoryRequirements, createInfo: AllocationCreateInfo) !Allocation {
        return allocateMemoryAndGetInfo(allocator, vkMemoryRequirements, createInfo, null);
    }
    pub fn allocateMemoryAndGetInfo(allocator: Allocator, vkMemoryRequirements: vk.MemoryRequirements, createInfo: AllocationCreateInfo, outInfo: ?*AllocationInfo) !Allocation {
        var result: Allocation = undefined;
        const rc = vmaAllocateMemory(allocator, &vkMemoryRequirements, &createInfo, &result, outInfo);
        if (@intFromEnum(rc) >= 0) return result;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief General purpose memory allocation for multiple allocation objects at once.
    ///
    /// @param allocator Allocator object.
    /// @param pVkMemoryRequirements Memory requirements for each allocation.
    /// @param pCreateInfo Creation parameters for each alloction.
    /// @param allocationCount Number of allocations to make.
    /// @param[out] pAllocations Pointer to array that will be filled with handles to created allocations.
    /// @param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations.
    ///
    /// You should free the memory using FreeMemory() or FreeMemoryPages().
    ///
    /// Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding.
    /// It is just a general purpose allocation function able to make multiple allocations at once.
    /// It may be internally optimized to be more efficient than calling AllocateMemory() `allocationCount` times.
    ///
    /// All allocations are made using same parameters. All of them are created out of the same memory pool and type.
    /// If any allocation fails, all allocations already made within this function call are also freed, so that when
    /// returned result is not `vk.SUCCESS`, `pAllocation` array is always entirely filled with `.Null`.
    pub fn allocateMemoryPages(allocator: Allocator, vkMemoryRequirements: vk.MemoryRequirements, createInfo: AllocationCreateInfo, outAllocations: []Allocation) !void {
        const rc = vmaAllocateMemoryPages(allocator, &vkMemoryRequirements, &createInfo, outAllocations.len, outAllocations.ptr, null);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }
    pub fn allocateMemoryPagesAndGetInfo(allocator: Allocator, vkMemoryRequirements: vk.MemoryRequirements, createInfo: AllocationCreateInfo, outAllocations: []Allocation, outInfo: []AllocationInfo) !void {
        assert(outAllocations.len == outInfo.len);
        const rc = vmaAllocateMemoryPages(allocator, &vkMemoryRequirements, &createInfo, outAllocations.len, outAllocations.ptr, outInfo.ptr);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// @param[out] pAllocation Handle to allocated memory.
    /// @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function GetAllocationInfo().
    ///
    /// You should free the memory using FreeMemory().
    pub fn allocateMemoryForBuffer(allocator: Allocator, buffer: vk.Buffer, createInfo: AllocationCreateInfo) !Allocation {
        return allocateMemoryForBufferAndGetInfo(allocator, buffer, createInfo, null);
    }
    pub fn allocateMemoryForBufferAndGetInfo(allocator: Allocator, buffer: vk.Buffer, createInfo: AllocationCreateInfo, outInfo: ?*AllocationInfo) !Allocation {
        var result: Allocation = undefined;
        const rc = vmaAllocateMemoryForBuffer(allocator, buffer, &createInfo, &result, outInfo);
        if (@intFromEnum(rc) >= 0) return result;

        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// Function similar to AllocateMemoryForBuffer().
    pub fn allocateMemoryForImage(allocator: Allocator, image: vk.Image, createInfo: AllocationCreateInfo) !Allocation {
        return allocateMemoryForImageAndGetInfo(allocator, image, createInfo, null);
    }
    pub fn allocateMemoryForImageAndGetInfo(allocator: Allocator, image: vk.Image, createInfo: AllocationCreateInfo, outInfo: ?*AllocationInfo) !Allocation {
        var result: Allocation = undefined;
        const rc = vmaAllocateMemoryForImage(allocator, image, &createInfo, &result, outInfo);
        if (@intFromEnum(rc) >= 0) return result;

        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Frees memory previously allocated using AllocateMemory(), AllocateMemoryForBuffer(), or AllocateMemoryForImage().
    ///
    /// Passing `.Null` as `allocation` is valid. Such function call is just skipped.
    /// fn freeMemory(allocator: Allocator, allocation: Allocation) void
    pub const freeMemory = vmaFreeMemory;

    /// \brief Frees memory and destroys multiple allocations.
    ///
    /// Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding.
    /// It is just a general purpose function to free memory and destroy allocations made using e.g. AllocateMemory(),
    /// AllocateMemoryPages() and other functions.
    /// It may be internally optimized to be more efficient than calling FreeMemory() `allocationCount` times.
    ///
    /// Allocations in `pAllocations` array can come from any memory pools and types.
    /// Passing `.Null` as elements of `pAllocations` array is valid. Such entries are just skipped.
    pub fn freeMemoryPages(allocator: Allocator, allocations: []Allocation) void {
        vmaFreeMemoryPages(allocator, allocations.len, allocations.ptr);
    }

    /// \brief Returns current information about specified allocation and atomically marks it as used in current frame.
    ///
    /// Current paramters of given allocation are returned in `pAllocationInfo`.
    ///
    /// This function also atomically "touches" allocation - marks it as used in current frame,
    /// just like TouchAllocation().
    /// If the allocation is in lost state, `pAllocationInfo->deviceMemory == .Null`.
    ///
    /// Although this function uses atomics and doesn't lock any mutex, so it should be quite efficient,
    /// you can avoid calling it too often.
    ///
    /// - You can retrieve same AllocationInfo structure while creating your resource, from function
    /// CreateBuffer(), CreateImage(). You can remember it if you are sure parameters don't change
    /// (e.g. due to defragmentation or allocation becoming lost).
    /// - If you just want to check if allocation is not lost, TouchAllocation() will work faster.
    pub fn getAllocationInfo(allocator: Allocator, allocation: Allocation) AllocationInfo {
        var info: AllocationInfo = undefined;
        vmaGetAllocationInfo(allocator, allocation, &info);
        return info;
    }

    /// \brief Returns `true` if allocation is not lost and atomically marks it as used in current frame.
    ///
    /// If the allocation has been created with #.canBecomeLost flag,
    /// this function returns `true` if it's not in lost state, so it can still be used.
    /// It then also atomically "touches" the allocation - marks it as used in current frame,
    /// so that you can be sure it won't become lost in current frame or next `frameInUseCount` frames.
    ///
    /// If the allocation is in lost state, the function returns `false`.
    /// Memory of such allocation, as well as buffer or image bound to it, should not be used.
    /// Lost allocation and the buffer/image still need to be destroyed.
    ///
    /// If the allocation has been created without #.canBecomeLost flag,
    /// this function always returns `true`.
    pub fn touchAllocation(allocator: Allocator, allocation: Allocation) bool {
        return vmaTouchAllocation(allocator, allocation) != 0;
    }

    /// \brief Sets pUserData in given allocation to new value.
    ///
    /// If the allocation was created with VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT,
    /// pUserData must be either null, or pointer to a null-terminated string. The function
    /// makes local copy of the string and sets it as allocation's `pUserData`. String
    /// passed as pUserData doesn't need to be valid for whole lifetime of the allocation -
    /// you can free it after this call. String previously pointed by allocation's
    /// pUserData is freed from memory.
    ///
    /// If the flag was not used, the value of pointer `pUserData` is just copied to
    /// allocation's `pUserData`. It is opaque, so you can use it however you want - e.g.
    /// as a pointer, ordinal number or some handle to you own data.
    /// fn setAllocationUserData(allocator: Allocator, allocation: Allocation, pUserData: ?*anyopaque) void
    pub const setAllocationUserData = vmaSetAllocationUserData;

    /// \brief Creates new allocation that is in lost state from the beginning.
    ///
    /// It can be useful if you need a dummy, non-null allocation.
    ///
    /// You still need to destroy created object using FreeMemory().
    ///
    /// Returned allocation is not tied to any specific memory pool or memory type and
    /// not bound to any image or buffer. It has size = 0. It cannot be turned into
    /// a real, non-empty allocation.
    pub fn createLostAllocation(allocator: Allocator) Allocation {
        var allocation: Allocation = undefined;
        vmaCreateLostAllocation(allocator, &allocation);
        return allocation;
    }

    /// \brief Maps memory represented by given allocation and returns pointer to it.
    ///
    /// Maps memory represented by given allocation to make it accessible to CPU code.
    /// When succeeded, `*ppData` contains pointer to first byte of this memory.
    /// If the allocation is part of bigger `vk.DeviceMemory` block, the pointer is
    /// correctly offseted to the beginning of region assigned to this particular
    /// allocation.
    ///
    /// Mapping is internally reference-counted and synchronized, so despite raw Vulkan
    /// function `vkMapMemory()` cannot be used to map same block of `vk.DeviceMemory`
    /// multiple times simultaneously, it is safe to call this function on allocations
    /// assigned to the same memory block. Actual Vulkan memory will be mapped on first
    /// mapping and unmapped on last unmapping.
    ///
    /// If the function succeeded, you must call UnmapMemory() to unmap the
    /// allocation when mapping is no longer needed or before freeing the allocation, at
    /// the latest.
    ///
    /// It also safe to call this function multiple times on the same allocation. You
    /// must call UnmapMemory() same number of times as you called MapMemory().
    ///
    /// It is also safe to call this function on allocation created with
    /// #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time.
    /// You must still call UnmapMemory() same number of times as you called
    /// MapMemory(). You must not call UnmapMemory() additional time to free the
    /// "0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag.
    ///
    /// This function fails when used on allocation made in memory type that is not
    /// `HOST_VISIBLE`.
    ///
    /// This function always fails when called for allocation that was created with
    /// #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocations cannot be
    /// mapped.
    ///
    /// This function doesn't automatically flush or invalidate caches.
    /// If the allocation is made from a memory types that is not `HOST_COHERENT`,
    /// you also need to use InvalidateAllocation() / FlushAllocation(), as required by Vulkan specification.
    pub fn mapMemory(allocator: Allocator, allocation: Allocation, comptime T: type) ![*]T {
        var data: *anyopaque = undefined;
        const rc = vmaMapMemory(allocator, allocation, &data);
        if (@intFromEnum(rc) >= 0) return @as([*]T, @ptrFromInt(@intFromPtr(data)));
        return error.VK_UNDOCUMENTED_ERROR;
        //return switch (rc) {
        //    .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
        //    .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
        //    .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
        //    else => error.VK_UNDOCUMENTED_ERROR,
        //};
    }

    /// \brief Unmaps memory represented by given allocation, mapped previously using MapMemory().
    ///
    /// For details, see description of MapMemory().
    ///
    /// This function doesn't automatically flush or invalidate caches.
    /// If the allocation is made from a memory types that is not `HOST_COHERENT`,
    /// you also need to use InvalidateAllocation() / FlushAllocation(), as required by Vulkan specification.
    /// fn unmapMemory(self: Allocator, allocation: Allocation) void
    pub const unmapMemory = vmaUnmapMemory;

    /// \brief Flushes memory of given allocation.
    ///
    /// Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation.
    /// It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`.
    /// Unmap operation doesn't do that automatically.
    ///
    /// - `offset` must be relative to the beginning of allocation.
    /// - `size` can be `vk.WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
    /// - `offset` and `size` don't have to be aligned.
    /// They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
    /// - If `size` is 0, this call is ignored.
    /// - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
    /// this call is ignored.
    ///
    /// Warning! `offset` and `size` are relative to the contents of given `allocation`.
    /// If you mean whole allocation, you can pass 0 and `vk.WHOLE_SIZE`, respectively.
    /// Do not pass allocation's offset as `offset`!!!
    /// fn flushAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) void
    pub const flushAllocation = vmaFlushAllocation;

    /// \brief Invalidates memory of given allocation.
    ///
    /// Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation.
    /// It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`.
    /// Map operation doesn't do that automatically.
    ///
    /// - `offset` must be relative to the beginning of allocation.
    /// - `size` can be `vk.WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation.
    /// - `offset` and `size` don't have to be aligned.
    /// They are internally rounded down/up to multiply of `nonCoherentAtomSize`.
    /// - If `size` is 0, this call is ignored.
    /// - If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`,
    /// this call is ignored.
    ///
    /// Warning! `offset` and `size` are relative to the contents of given `allocation`.
    /// If you mean whole allocation, you can pass 0 and `vk.WHOLE_SIZE`, respectively.
    /// Do not pass allocation's offset as `offset`!!!
    /// fn invalidateAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) void
    pub const invalidateAllocation = vmaInvalidateAllocation;

    /// \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
    ///
    /// @param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked.
    ///
    /// Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero,
    /// `VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are
    /// `HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection).
    ///
    /// Possible return values:
    ///
    /// - `error.VK_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.
    /// - `vk.SUCCESS` - corruption detection has been performed and succeeded.
    /// - `error.VK_VALIDATION_FAILED_EXT` - corruption detection has been performed and found memory corruptions around one of the allocations.
    /// `VMA_ASSERT` is also fired in that case.
    /// - Other value: Error returned by Vulkan, e.g. memory mapping failure.
    pub fn checkCorruption(allocator: Allocator, memoryTypeBits: u32) !void {
        const rc = vmaCheckCorruption(allocator, memoryTypeBits);
        if (@intFromEnum(rc) >= 0) return;

        return switch (rc) {
            .error_feature_not_present => error.VMA_CORRUPTION_DETECTION_DISABLED,
            .error_validation_failed_ext => error.VMA_CORRUPTION_DETECTED,
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Begins defragmentation process.
    ///
    /// @param allocator Allocator object.
    /// @param pInfo Structure filled with parameters of defragmentation.
    /// @param[out] pStats Optional. Statistics of defragmentation. You can pass null if you are not interested in this information.
    /// @param[out] pContext Context object that must be passed to DefragmentationEnd() to finish defragmentation.
    /// @return `vk.SUCCESS` and `*pContext == null` if defragmentation finished within this function call. `vk.NOT_READY` and `*pContext != null` if defragmentation has been started and you need to call DefragmentationEnd() to finish it. Negative value in case of error.
    ///
    /// Use this function instead of old, deprecated Defragment().
    ///
    /// Warning! Between the call to DefragmentationBegin() and DefragmentationEnd():
    ///
    /// - You should not use any of allocations passed as `pInfo->pAllocations` or
    ///   any allocations that belong to pools passed as `pInfo->pPools`,
    ///   including calling GetAllocationInfo(), TouchAllocation(), or access
    ///   their data.
    /// - Some mutexes protecting internal data structures may be locked, so trying to
    ///   make or free any allocations, bind buffers or images, map memory, or launch
    ///   another simultaneous defragmentation in between may cause stall (when done on
    ///   another thread) or deadlock (when done on the same thread), unless you are
    ///   100% sure that defragmented allocations are in different pools.
    /// - Information returned via `pStats` and `pInfo->pAllocationsChanged` are undefined.
    ///   They become valid after call to DefragmentationEnd().
    /// - If `pInfo->commandBuffer` is not null, you must submit that command buffer
    ///   and make sure it finished execution before calling DefragmentationEnd().
    ///
    /// For more information and important limitations regarding defragmentation, see documentation chapter:
    /// [Defragmentation](@ref defragmentation).
    pub fn defragmentationBegin(allocator: Allocator, info: DefragmentationInfo2) !DefragmentationContext {
        return defragmentationBeginWithStats(allocator, info, null);
    }
    pub fn defragmentationBeginWithStats(allocator: Allocator, info: DefragmentationInfo2, stats: ?*DefragmentationStats) !DefragmentationContext {
        var context: DefragmentationContext = undefined;
        const rc = vmaDefragmentationBegin(allocator, &info, stats, &context);
        if (@intFromEnum(rc) >= 0) return context; // includes NOT_READY
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Ends defragmentation process.
    ///
    /// Use this function to finish defragmentation started by DefragmentationBegin().
    /// It is safe to pass `context == null`. The function then does nothing.
    pub fn defragmentationEnd(allocator: Allocator, context: DefragmentationContext) !void {
        const rc = vmaDefragmentationEnd(allocator, context);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
            .error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            .error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
            .error_fragmented_pool => error.VK_FRAGMENTED_POOL,
            .error_out_of_pool_memory => error.VK_OUT_OF_POOL_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Binds buffer to allocation.
    ///
    /// Binds specified buffer to region of memory represented by specified allocation.
    /// Gets `vk.DeviceMemory` handle and offset from the allocation.
    /// If you want to create a buffer, allocate memory for it and bind them together separately,
    /// you should use this function for binding instead of standard `vkBindBufferMemory()`,
    /// because it ensures proper synchronization so that when a `vk.DeviceMemory` object is used by multiple
    /// allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
    /// (which is illegal in Vulkan).
    ///
    /// It is recommended to use function createBuffer() instead of this one.
    pub fn bindBufferMemory(allocator: Allocator, allocation: Allocation, buffer: vk.Buffer) !void {
        const rc = vmaBindBufferMemory(allocator, allocation, buffer);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Binds buffer to allocation with additional parameters.
    ///
    /// @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
    /// @param pNext A chain of structures to be attached to `vk.BindBufferMemoryInfoKHR` structure used internally. Normally it should be null.
    ///
    /// This function is similar to BindBufferMemory(), but it provides additional parameters.
    ///
    /// If `pNext` is not null, #Allocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
    /// or with AllocatorCreateInfo::vulkanApiVersion `== vk.API_VERSION_1_1`. Otherwise the call fails.
    pub fn bindBufferMemory2(allocator: Allocator, allocation: Allocation, allocationLocalOffset: vk.DeviceSize, buffer: vk.Buffer, pNext: ?*const anyopaque) !void {
        const rc = vmaBindBufferMemory2(allocator, allocation, allocationLocalOffset, buffer, pNext);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Binds image to allocation.
    ///
    /// Binds specified image to region of memory represented by specified allocation.
    /// Gets `vk.DeviceMemory` handle and offset from the allocation.
    /// If you want to create an image, allocate memory for it and bind them together separately,
    /// you should use this function for binding instead of standard `vkBindImageMemory()`,
    /// because it ensures proper synchronization so that when a `vk.DeviceMemory` object is used by multiple
    /// allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously
    /// (which is illegal in Vulkan).
    ///
    /// It is recommended to use function CreateImage() instead of this one.
    pub fn bindImageMemory(allocator: Allocator, allocation: Allocation, image: vk.Image) !void {
        const rc = vmaBindImageMemory(allocator, allocation, image);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// \brief Binds image to allocation with additional parameters.
    ///
    /// @param allocationLocalOffset Additional offset to be added while binding, relative to the beginnig of the `allocation`. Normally it should be 0.
    /// @param pNext A chain of structures to be attached to `vk.BindImageMemoryInfoKHR` structure used internally. Normally it should be null.
    ///
    /// This function is similar to BindImageMemory(), but it provides additional parameters.
    ///
    /// If `pNext` is not null, #Allocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag
    /// or with AllocatorCreateInfo::vulkanApiVersion `== vk.API_VERSION_1_1`. Otherwise the call fails.
    pub fn bindImageMemory2(allocator: Allocator, allocation: Allocation, allocationLocalOffset: vk.DeviceSize, image: vk.Image, pNext: ?*const anyopaque) !void {
        const rc = vmaBindImageMemory2(allocator, allocation, allocationLocalOffset, image, pNext);
        if (@intFromEnum(rc) >= 0) return;
        return switch (rc) {
            .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
            .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
            .error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
            else => error.VK_UNDOCUMENTED_ERROR,
        };
    }

    /// @param[out] pBuffer Buffer that was created.
    /// @param[out] pAllocation Allocation that was created.
    /// @param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function GetAllocationInfo().
    ///
    /// This function automatically:
    ///
    /// -# Creates buffer.
    /// -# Allocates appropriate memory for it.
    /// -# Binds the buffer with the memory.
    ///
    /// If any of these operations fail, buffer and allocation are not created,
    /// returned value is negative error code, *pBuffer and *pAllocation are null.
    ///
    /// If the function succeeded, you must destroy both buffer and allocation when you
    /// no longer need them using either convenience function DestroyBuffer() or
    /// separately, using `vkDestroyBuffer()` and FreeMemory().
    ///
    /// If VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used,
    /// vk.KHR_dedicated_allocation extension is used internally to query driver whether
    /// it requires or prefers the new buffer to have dedicated allocation. If yes,
    /// and if dedicated allocation is possible (AllocationCreateInfo::pool is null
    /// and VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated
    /// allocation for this buffer, just like when using
    /// VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
    pub fn createBuffer(
        allocator: Allocator,
        bufferCreateInfo: vk.BufferCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
    ) !CreateBufferResult {
        return createBufferAndGetInfo(allocator, bufferCreateInfo, allocationCreateInfo, null);
    }
    pub fn createBufferAndGetInfo(
        allocator: Allocator,
        bufferCreateInfo: vk.BufferCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
        outInfo: ?*AllocationInfo,
    ) !CreateBufferResult {
        var result: CreateBufferResult = undefined;
        const rc = vmaCreateBuffer(
            allocator,
            &bufferCreateInfo,
            &allocationCreateInfo,
            &result.buffer,
            &result.allocation,
            outInfo,
        );
        if (@intFromEnum(rc) >= 0) return result;
        return error.VK_UNDOCUMENTED_ERROR;
        //switch (rc) {
        //.error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
        //.error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
        //.error_too_many_objects => error.VK_TOO_MANY_OBJECTS,
        //.error_invalid_external_handle => error.VK_INVALID_EXTERNAL_HANDLE,
        //.error_invalid_opaque_capture_address => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
        //.error_memory_map_failed => error.VK_MEMORY_MAP_FAILED,
        //.ERROR_FRAGMENTED_POOL => error.VK_FRAGMENTED_POOL,
        //.ERROR_OUT_OF_POOL_MEMORY => error.VK_OUT_OF_POOL_MEMORY,
        //else => error.VK_UNDOCUMENTED_ERROR,
        //};
    }
    pub const CreateBufferResult = struct {
        buffer: vk.Buffer,
        allocation: Allocation,
    };

    /// \brief Destroys Vulkan buffer and frees allocated memory.
    ///
    /// This is just a convenience function equivalent to:
    ///
    /// \code
    /// vkDestroyBuffer(device, buffer, allocationCallbacks);
    /// FreeMemory(allocator, allocation);
    /// \endcode
    ///
    /// It it safe to pass null as buffer and/or allocation.
    /// fn destroyBuffer(allocator: Allocator, buffer: vk.Buffer, allocation: Allocation) void
    pub const destroyBuffer = vmaDestroyBuffer;

    /// Function similar to CreateBuffer().
    pub fn createImage(
        allocator: Allocator,
        imageCreateInfo: vk.ImageCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
    ) !CreateImageResult {
        return createImageAndGetInfo(allocator, imageCreateInfo, allocationCreateInfo, null);
    }
    pub fn createImageAndGetInfo(
        allocator: Allocator,
        imageCreateInfo: vk.ImageCreateInfo,
        allocationCreateInfo: AllocationCreateInfo,
        outInfo: ?*AllocationInfo,
    ) !CreateImageResult {
        var result: CreateImageResult = undefined;
        const rc = vmaCreateImage(
            allocator,
            &imageCreateInfo,
            &allocationCreateInfo,
            &result.image,
            &result.allocation,
            outInfo,
        );
        if (@intFromEnum(rc) >= 0) return result;
        std.debug.print("vulkan error = {d}\n", .{rc});
        return error.VK_UNDOCUMENTED_ERROR;
        // return switch (rc) {
        //     .error_out_of_host_memory => error.VK_OUT_OF_HOST_MEMORY,
        //     .error_out_of_device_memory => error.VK_OUT_OF_DEVICE_MEMORY,
        //     .ERROR_TOO_MANY_OBJECTS => error.VK_TOO_MANY_OBJECTS,
        //     .ERROR_INVALID_EXTERNAL_HANDLE => error.VK_INVALID_EXTERNAL_HANDLE,
        //     .ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS => error.VK_INVALID_OPAQUE_CAPTURE_ADDRESS,
        //     .ERROR_MEMORY_MAP_FAILED => error.VK_MEMORY_MAP_FAILED,
        //     .ERROR_FRAGMENTED_POOL => error.VK_FRAGMENTED_POOL,
        //     .ERROR_OUT_OF_POOL_MEMORY => error.VK_OUT_OF_POOL_MEMORY,
        //     else => error.VK_UNDOCUMENTED_ERROR,
        // };
    }
    pub const CreateImageResult = struct {
        image: vk.Image,
        allocation: Allocation,
    };

    /// \brief Destroys Vulkan image and frees allocated memory.
    ///
    /// This is just a convenience function equivalent to:
    ///
    /// \code
    /// vkDestroyImage(device, image, allocationCallbacks);
    /// FreeMemory(allocator, allocation);
    /// \endcode
    ///
    /// It is safe to pass null as image and/or allocation.
    /// fn destroyImage(self: Allocator, image: vk.Image, allocation: Allocation) void
    pub const destroyImage = vmaDestroyImage;
};

/// Callback function called after successful vkAllocateMemory.
pub const PfnAllocateDeviceMemoryFunction = *const fn (
    allocator: Allocator,
    memoryType: u32,
    memory: vk.DeviceMemory,
    size: vk.DeviceSize,
) callconv(vulkan_call_conv) void;

/// Callback function called before vkFreeMemory.
pub const PfnFreeDeviceMemoryFunction = *const fn (
    allocator: Allocator,
    memoryType: u32,
    memory: vk.DeviceMemory,
    size: vk.DeviceSize,
) callconv(vulkan_call_conv) void;

/// \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`.
///
/// Provided for informative purpose, e.g. to gather statistics about number of
/// allocations or total amount of memory allocated in Vulkan.
///
/// Used in AllocatorCreateInfo::pDeviceMemoryCallbacks.
pub const DeviceMemoryCallbacks = extern struct {
    pfnAllocate: ?PfnAllocateDeviceMemoryFunction,
    pfnFree: ?PfnFreeDeviceMemoryFunction,
};

/// Flags for created #Allocator.
pub const AllocatorCreateFlags = packed struct {
    /// \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you.
    ///
    /// Using this flag may increase performance because internal mutexes are not used.
    externallySynchronized: bool = false,

    /// \brief Enables usage of vk.KHR_dedicated_allocation extension.
    ///
    /// The flag works only if AllocatorCreateInfo::vulkanApiVersion `== vk.API_VERSION_1_0`.
    /// When it's `vk.API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
    ///
    /// Using this extenion will automatically allocate dedicated blocks of memory for
    /// some buffers and images instead of suballocating place for them out of bigger
    /// memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT
    /// flag) when it is recommended by the driver. It may improve performance on some
    /// GPUs.
    ///
    /// You may set this flag only if you found out that following device extensions are
    /// supported, you enabled them while creating Vulkan device passed as
    /// AllocatorCreateInfo::device, and you want them to be used internally by this
    /// library:
    ///
    /// - vk.KHR_get_memory_requirements2 (device extension)
    /// - vk.KHR_dedicated_allocation (device extension)
    ///
    /// When this flag is set, you can experience following warnings reported by Vulkan
    /// validation layer. You can ignore them.
    ///
    /// > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer.
    dedicatedAllocationKHR: bool = false,

    /// Enables usage of vk.KHR_bind_memory2 extension.
    ///
    /// The flag works only if AllocatorCreateInfo::vulkanApiVersion `== vk.API_VERSION_1_0`.
    /// When it's `vk.API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.
    ///
    /// You may set this flag only if you found out that this device extension is supported,
    /// you enabled it while creating Vulkan device passed as AllocatorCreateInfo::device,
    /// and you want it to be used internally by this library.
    ///
    /// The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`,
    /// which allow to pass a chain of `pNext` structures while binding.
    /// This flag is required if you use `pNext` parameter in BindBufferMemory2() or BindImageMemory2().
    bindMemory2KHR: bool = false,

    /// Enables usage of vk.EXT_memory_budget extension.
    ///
    /// You may set this flag only if you found out that this device extension is supported,
    /// you enabled it while creating Vulkan device passed as AllocatorCreateInfo::device,
    /// and you want it to be used internally by this library, along with another instance extension
    /// vk.KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted).
    ///
    /// The extension provides query for current memory usage and budget, which will probably
    /// be more accurate than an estimation used by the library otherwise.
    memoryBudgetEXT: bool = false,

    __reserved_bits_04_31: u28 = 0,

    pub usingnamespace vk.FlagsMixin(@This(), vk.Flags);
};

/// \brief Pointers to some Vulkan functions - a subset used by the library.
///
/// Used in AllocatorCreateInfo::pVulkanFunctions.
pub const VulkanFunctions = extern struct {
    vkGetPhysicalDeviceProperties: vk.PfnGetPhysicalDeviceProperties,
    vkGetPhysicalDeviceMemoryProperties: vk.PfnGetPhysicalDeviceMemoryProperties,
    vkAllocateMemory: vk.PfnAllocateMemory,
    vkFreeMemory: vk.PfnFreeMemory,
    vkMapMemory: vk.PfnMapMemory,
    vkUnmapMemory: vk.PfnUnmapMemory,
    vkFlushMappedMemoryRanges: vk.PfnFlushMappedMemoryRanges,
    vkInvalidateMappedMemoryRanges: vk.PfnInvalidateMappedMemoryRanges,
    vkBindBufferMemory: vk.PfnBindBufferMemory,
    vkBindImageMemory: vk.PfnBindImageMemory,
    vkGetBufferMemoryRequirements: vk.PfnGetBufferMemoryRequirements,
    vkGetImageMemoryRequirements: vk.PfnGetImageMemoryRequirements,
    vkCreateBuffer: vk.PfnCreateBuffer,
    vkDestroyBuffer: vk.PfnDestroyBuffer,
    vkCreateImage: vk.PfnCreateImage,
    vkDestroyImage: vk.PfnDestroyImage,
    vkCmdCopyBuffer: vk.PfnCmdCopyBuffer,

    dedicatedAllocation: if (config.dedicatedAllocation or config.vulkanVersion >= 1001000) DedicatedAllocationFunctions else void,
    bindMemory2: if (config.bindMemory2 or config.vulkanVersion >= 1001000) BindMemory2Functions else void,
    memoryBudget: if (config.memoryBudget or config.vulkanVersion >= 1001000) MemoryBudgetFunctions else void,

    const DedicatedAllocationFunctions = extern struct {
        vkGetBufferMemoryRequirements2: vk.PfnGetBufferMemoryRequirements2,
        vkGetImageMemoryRequirements2: vk.PfnGetImageMemoryRequirements2,
    };
    const BindMemory2Functions = extern struct {
        vkBindBufferMemory2: vk.PfnBindBufferMemory2,
        vkBindImageMemory2: vk.PfnBindImageMemory2,
    };
    const MemoryBudgetFunctions = extern struct {
        vkGetPhysicalDeviceMemoryProperties2: vk.PfnGetPhysicalDeviceMemoryProperties2,
    };

    fn isDeviceFunc(comptime FuncType: type) bool {
        comptime {
            const info = @typeInfo(@typeInfo(FuncType).Pointer.child).Fn;
            if (info.params.len == 0) return false;
            const arg0 = info.params[0].type;
            return arg0 == vk.Device or arg0 == vk.Queue or arg0 == vk.CommandBuffer;
        }
    }

    fn loadRecursive(
        comptime T: type,
        inst: vk.Instance,
        device: vk.Device,
        vkGetInstanceProcAddr: *const fn (vk.Instance, [*:0]const u8) callconv(vk.vulkan_call_conv) vk.PfnVoidFunction,
        vkGetDeviceProcAddr: *const fn (vk.Device, [*:0]const u8) callconv(vulkan_call_conv) vk.PfnVoidFunction,
    ) T {
        var funcStringBuffer: [4096]u8 = std.mem.zeroes([4096]u8);

        if (@typeInfo(T) != .Struct) return undefined;
        var value: T = undefined;
        inline for (@typeInfo(T).Struct.fields) |field| {
            if (comptime std.mem.startsWith(u8, field.name, "vk")) {
                @memcpy((&funcStringBuffer).ptr, field.name);
                funcStringBuffer[field.name.len] = 0;
                if (comptime isDeviceFunc(field.type)) {
                    const func = vkGetDeviceProcAddr(device, @ptrCast(&funcStringBuffer));
                    const resolved = func orelse @panic("Couldn't fetch vk device function " ++ field.name);
                    @field(value, field.name) = @as(field.type, @ptrCast(resolved));
                } else {
                    const func = vkGetInstanceProcAddr(inst, @ptrCast(&funcStringBuffer));
                    const resolved = func orelse @panic("Couldn't fetch vk instance function " ++ field.name);
                    @field(value, field.name) = @as(field.type, @ptrCast(resolved));
                }
            } else {
                @field(value, field.name) = loadRecursive(field.type, inst, device, vkGetInstanceProcAddr, vkGetDeviceProcAddr);
            }
        }
        return value;
    }

    pub fn init(
        inst: vk.Instance,
        device: vk.Device,
        vkGetInstanceProcAddr: *const fn (vk.Instance, [*:0]const u8) callconv(vulkan_call_conv) vk.PfnVoidFunction,
    ) VulkanFunctions {
        const vkGetDeviceProcAddrPtr = vkGetInstanceProcAddr(inst, "vkGetDeviceProcAddr") orelse @panic("Couldn't fetch vkGetDeviceProcAddr: vkGetInstanceProcAddr returned null.");
        const vkGetDeviceProcAddr = @as(*const fn (vk.Device, [*:0]const u8) callconv(vulkan_call_conv) vk.PfnVoidFunction, @ptrCast(vkGetDeviceProcAddrPtr));
        return loadRecursive(VulkanFunctions, inst, device, vkGetInstanceProcAddr, vkGetDeviceProcAddr);
    }
};

/// Flags to be used in RecordSettings::flags.
pub const RecordFlags = packed struct {
    /// \brief Enables flush after recording every function call.
    ///
    /// Enable it if you expect your application to crash, which may leave recording file truncated.
    /// It may degrade performance though.
    flushAfterCall: bool = false,

    __reserved_bits_01_31: u31 = 0,

    pub usingnamespace vk.FlagsMixin(@This(), vk.Flags);
};

/// Parameters for recording calls to VMA functions. To be used in AllocatorCreateInfo::pRecordSettings.
pub const RecordSettings = extern struct {
    /// Flags for recording. Use #RecordFlagBits enum.
    flags: RecordFlags = .{},
    /// \brief Path to the file that should be written by the recording.
    ///
    /// Suggested extension: "csv".
    /// If the file already exists, it will be overwritten.
    /// It will be opened for the whole time #Allocator object is alive.
    /// If opening this file fails, creation of the whole allocator object fails.
    pFilePath: [*:0]const u8,
};

/// Description of a Allocator to be created.
pub const AllocatorCreateInfo = extern struct {
    /// Flags for created allocator. Use #AllocatorCreateFlagBits enum.
    flags: AllocatorCreateFlags align(4) = .{},
    /// Vulkan physical device.
    /// It must be valid throughout whole lifetime of created allocator.
    physicalDevice: vk.PhysicalDevice,
    /// Vulkan device.
    /// It must be valid throughout whole lifetime of created allocator.
    device: vk.Device,
    /// Preferred size of a single `vk.DeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.
    /// Set to 0 to use default, which is currently 256 MiB.
    preferredLargeHeapBlockSize: vk.DeviceSize = 0,
    /// Custom CPU memory allocation callbacks. Optional.
    /// Optional, can be null. When specified, will also be used for all CPU-side memory allocations.
    pAllocationCallbacks: ?*const vk.AllocationCallbacks = null,
    /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.
    /// Optional, can be null.
    pDeviceMemoryCallbacks: ?*const DeviceMemoryCallbacks = null,
    /// \brief Maximum number of additional frames that are in use at the same time as current frame.
    ///
    /// This value is used only when you make allocations with
    /// .canBeLost = true. Such allocation cannot become
    /// lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
    ///
    /// For example, if you double-buffer your command buffers, so resources used for
    /// rendering in previous frame may still be in use by the GPU at the moment you
    /// allocate resources needed for the current frame, set this value to 1.
    ///
    /// If you want to allow any allocations other than used in the current frame to
    /// become lost, set this value to 0.
    frameInUseCount: u32,
    /// \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap.
    ///
    /// If not NULL, it must be a pointer to an array of
    /// `vk.PhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on
    /// maximum number of bytes that can be allocated out of particular Vulkan memory
    /// heap.
    ///
    /// Any of the elements may be equal to `vk.WHOLE_SIZE`, which means no limit on that
    /// heap. This is also the default in case of `pHeapSizeLimit` = NULL.
    ///
    /// If there is a limit defined for a heap:
    ///
    /// - If user tries to allocate more memory from that heap using this allocator,
    /// the allocation fails with `error.VK_OUT_OF_DEVICE_MEMORY`.
    /// - If the limit is smaller than heap size reported in `vk.MemoryHeap::size`, the
    /// value of this limit will be reported instead when using GetMemoryProperties().
    ///
    /// Warning! Using this feature may not be equivalent to installing a GPU with
    /// smaller amount of memory, because graphics driver doesn't necessary fail new
    /// allocations with `error.VK_OUT_OF_DEVICE_MEMORY` result when memory capacity is
    /// exceeded. It may return success and just silently migrate some device memory
    /// blocks to system RAM. This driver behavior can also be controlled using
    /// vk.AMD_memory_overallocation_behavior extension.
    pHeapSizeLimit: ?[*]const vk.DeviceSize = null,
    /// \brief Pointers to Vulkan functions. Can be null if you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1`.
    ///
    /// If you leave define `VMA_STATIC_VULKAN_FUNCTIONS 1` in configuration section,
    /// you can pass null as this member, because the library will fetch pointers to
    /// Vulkan functions internally in a static way, like:
    ///
    /// vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;
    ///
    /// Fill this member if you want to provide your own pointers to Vulkan functions,
    /// e.g. fetched using `vkGetInstanceProcAddr()` and `vkGetDeviceProcAddr()`.
    pVulkanFunctions: ?*const VulkanFunctions = null,
    /// \brief Parameters for recording of VMA calls. Can be null.
    ///
    /// If not null, it enables recording of calls to VMA functions to a file.
    /// If support for recording is not enabled using `VMA_RECORDING_ENABLED` macro,
    /// creation of the allocator object fails with `error.VK_FEATURE_NOT_PRESENT`.
    pRecordSettings: ?*const RecordSettings = null,
    /// \brief Optional handle to Vulkan instance object.
    ///
    /// Optional, can be null. Must be set if #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT flas is used
    /// or if `vulkanApiVersion >= vk.MAKE_VERSION(1, 1, 0)`.
    instance: vk.Instance,
    /// \brief Optional. The highest version of Vulkan that the application is designed to use.
    ///
    /// It must be a value in the format as created by macro `vk.MAKE_VERSION` or a constant like: `vk.API_VERSION_1_1`, `vk.API_VERSION_1_0`.
    /// The patch version number specified is ignored. Only the major and minor versions are considered.
    /// It must be less or euqal (preferably equal) to value as passed to `vkCreateInstance` as `vk.ApplicationInfo::apiVersion`.
    /// Only versions 1.0 and 1.1 are supported by the current implementation.
    /// Leaving it initialized to zero is equivalent to `vk.API_VERSION_1_0`.
    vulkanApiVersion: u32 = 0,
};

/// \brief Calculated statistics of memory usage in entire allocator.
pub const StatInfo = extern struct {
    /// Number of `vk.DeviceMemory` Vulkan memory blocks allocated.
    blockCount: u32,
    /// Number of #Allocation allocation objects allocated.
    allocationCount: u32,
    /// Number of free ranges of memory between allocations.
    unusedRangeCount: u32,
    /// Total number of bytes occupied by all allocations.
    usedBytes: vk.DeviceSize,
    /// Total number of bytes occupied by unused ranges.
    unusedBytes: vk.DeviceSize,

    allocationSizeMin: vk.DeviceSize,
    allocationSizeAvg: vk.DeviceSize,
    allocationSizeMax: vk.DeviceSize,
    unusedRangeSizeMin: vk.DeviceSize,
    unusedRangeSizeAvg: vk.DeviceSize,
    unusedRangeSizeMax: vk.DeviceSize,
};

/// General statistics from current state of Allocator.
pub const Stats = extern struct {
    memoryType: [vk.MAX_MEMORY_TYPES]StatInfo,
    memoryHeap: [vk.MAX_MEMORY_HEAPS]StatInfo,
    total: StatInfo,
};

/// \brief Statistics of current memory usage and available budget, in bytes, for specific memory heap.
pub const Budget = extern struct {
    /// \brief Sum size of all `vk.DeviceMemory` blocks allocated from particular heap, in bytes.
    blockBytes: vk.DeviceSize,

    /// \brief Sum size of all allocations created in particular heap, in bytes.
    ///
    /// Usually less or equal than `blockBytes`.
    /// Difference `blockBytes - allocationBytes` is the amount of memory allocated but unused -
    /// available for new allocations or wasted due to fragmentation.
    ///
    /// It might be greater than `blockBytes` if there are some allocations in lost state, as they account
    /// to this value as well.
    allocationBytes: vk.DeviceSize,

    /// \brief Estimated current memory usage of the program, in bytes.
    ///
    /// Fetched from system using `vk.EXT_memory_budget` extension if enabled.
    ///
    /// It might be different than `blockBytes` (usually higher) due to additional implicit objects
    /// also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or
    /// `vk.DeviceMemory` blocks allocated outside of this library, if any.
    usage: vk.DeviceSize,

    /// \brief Estimated amount of memory available to the program, in bytes.
    ///
    /// Fetched from system using `vk.EXT_memory_budget` extension if enabled.
    ///
    /// It might be different (most probably smaller) than `vk.MemoryHeap::size[heapIndex]` due to factors
    /// external to the program, like other programs also consuming system resources.
    /// Difference `budget - usage` is the amount of additional memory that can probably
    /// be allocated without problems. Exceeding the budget may result in various problems.
    budget: vk.DeviceSize,
};

/// \struct Pool
/// \brief Represents custom memory pool
///
/// Fill structure PoolCreateInfo and call function CreatePool() to create it.
/// Call function DestroyPool() to destroy it.
///
/// For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).
pub const Pool = enum(usize) { Null = 0, _ };

pub const MemoryUsage = enum(u32) {
    /// No intended memory usage specified.
    /// Use other members of AllocationCreateInfo to specify your requirements.
    unknown = 0,
    /// Memory will be used on device only, so fast access from the device is preferred.
    /// It usually means device-local GPU (video) memory.
    /// No need to be mappable on host.
    /// It is roughly equivalent of `D3D12_HEAP_TYPE_DEFAULT`.
    ///
    /// Usage:
    ///
    /// - Resources written and read by device, e.g. images used as attachments.
    /// - Resources transferred from host once (immutable) or infrequently and read by
    /// device multiple times, e.g. textures to be sampled, vertex buffers, uniform
    /// (constant) buffers, and majority of other types of resources used on GPU.
    ///
    /// Allocation may still end up in `HOST_VISIBLE` memory on some implementations.
    /// In such case, you are free to map it.
    /// You can use #VMA_ALLOCATION_CREATE_MAPPED_BIT with this usage type.
    gpuOnly = 1,
    /// Memory will be mappable on host.
    /// It usually means CPU (system) memory.
    /// Guarantees to be `HOST_VISIBLE` and `HOST_COHERENT`.
    /// CPU access is typically uncached. Writes may be write-combined.
    /// Resources created in this pool may still be accessible to the device, but access to them can be slow.
    /// It is roughly equivalent of `D3D12_HEAP_TYPE_UPLOAD`.
    ///
    /// Usage: Staging copy of resources used as transfer source.
    cpuOnly = 2,
    /// Memory that is both mappable on host (guarantees to be `HOST_VISIBLE`) and preferably fast to access by GPU.
    /// CPU access is typically uncached. Writes may be write-combined.
    ///
    /// Usage: Resources written frequently by host (dynamic), read by device. E.g. textures, vertex buffers, uniform buffers updated every frame or every draw call.
    cpuToGpu = 3,
    /// Memory mappable on host (guarantees to be `HOST_VISIBLE`) and cached.
    /// It is roughly equivalent of `D3D12_HEAP_TYPE_READBACK`.
    ///
    /// Usage:
    ///
    /// - Resources written by device, read by host - results of some computations, e.g. screen capture, average scene luminance for HDR tone mapping.
    /// - Any resources read or accessed randomly on host, e.g. CPU-side copy of vertex buffer used as source of transfer, but also used for collision detection.
    gpuToCpu = 4,
    /// CPU memory - memory that is preferably not `DEVICE_LOCAL`, but also not guaranteed to be `HOST_VISIBLE`.
    ///
    /// Usage: Staging copy of resources moved from GPU memory to CPU memory as part
    /// of custom paging/residency mechanism, to be moved back to GPU memory when needed.
    cpuCopy = 5,
    /// Lazily allocated GPU memory having `vk.MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`.
    /// Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation.
    ///
    /// Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `vk.IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`.
    ///
    /// Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.
    gpuLazilyAllocated = 6,
};

/// Flags to be passed as AllocationCreateInfo::flags.
pub const AllocationCreateFlags = packed struct {
    /// \brief Set this flag if the allocation should have its own memory block.
    ///
    /// Use it for special, big resources, like fullscreen images used as attachments.
    ///
    /// You should not use this flag if AllocationCreateInfo::pool is not null.
    dedicatedMemory: bool = false,

    /// \brief Set this flag to only try to allocate from existing `vk.DeviceMemory` blocks and never create new such block.
    ///
    /// If new allocation cannot be placed in any of the existing blocks, allocation
    /// fails with `error.VK_OUT_OF_DEVICE_MEMORY` error.
    ///
    /// You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and
    /// #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense.
    ///
    /// If AllocationCreateInfo::pool is not null, this flag is implied and ignored. */
    neverAllocate: bool = false,
    /// \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it.
    ///
    /// Pointer to mapped memory will be returned through AllocationInfo::pMappedData.
    ///
    /// Is it valid to use this flag for allocation made from memory type that is not
    /// `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
    /// useful if you need an allocation that is efficient to use on GPU
    /// (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
    /// support it (e.g. Intel GPU).
    ///
    /// You should not use this flag together with #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT.
    createMapped: bool = false,
    /// Allocation created with this flag can become lost as a result of another
    /// allocation with #VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT flag, so you
    /// must check it before use.
    ///
    /// To check if allocation is not lost, call GetAllocationInfo() and check if
    /// AllocationInfo::deviceMemory is not `.Null`.
    ///
    /// For details about supporting lost allocations, see Lost Allocations
    /// chapter of User Guide on Main Page.
    ///
    /// You should not use this flag together with #VMA_ALLOCATION_CREATE_MAPPED_BIT.
    canBecomeLost: bool = false,
    /// While creating allocation using this flag, other allocations that were
    /// created with flag #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT can become lost.
    ///
    /// For details about supporting lost allocations, see Lost Allocations
    /// chapter of User Guide on Main Page.
    canMakeOtherLost: bool = false,
    /// Set this flag to treat AllocationCreateInfo::pUserData as pointer to a
    /// null-terminated string. Instead of copying pointer value, a local copy of the
    /// string is made and stored in allocation's `pUserData`. The string is automatically
    /// freed together with the allocation. It is also used in BuildStatsString().
    userDataCopyString: bool = false,
    /// Allocation will be created from upper stack in a double stack pool.
    ///
    /// This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag.
    upperAddress: bool = false,
    /// Create both buffer/image and allocation, but don't bind them together.
    /// It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions.
    /// The flag is meaningful only with functions that bind by default: CreateBuffer(), CreateImage().
    /// Otherwise it is ignored.
    dontBind: bool = false,
    /// Create allocation only if additional device memory required for it, if any, won't exceed
    /// memory budget. Otherwise return `error.VK_OUT_OF_DEVICE_MEMORY`.
    withinBudget: bool = false,

    __reserved_bits_09_15: u7 = 0,

    /// Allocation strategy that chooses smallest possible free range for the
    /// allocation.
    strategyBestFit: bool = false,
    /// Allocation strategy that chooses biggest possible free range for the
    /// allocation.
    strategyWorstFit: bool = false,
    /// Allocation strategy that chooses first suitable free range for the
    /// allocation.
    ///
    /// "First" doesn't necessarily means the one with smallest offset in memory,
    /// but rather the one that is easiest and fastest to find.
    strategyFirstFit: bool = false,

    __reserved_bits_19_31: u13 = 0,

    /// Allocation strategy that tries to minimize memory usage.
    pub const STRATEGY_MIN_MEMORY = AllocationCreateFlags{ .strategyBestFit = true };
    /// Allocation strategy that tries to minimize allocation time.
    pub const STRATEGY_MIN_TIME = AllocationCreateFlags{ .strategyFirstFit = true };
    /// Allocation strategy that tries to minimize memory fragmentation.
    pub const STRATEGY_MIN_FRAGMENTATION = AllocationCreateFlags{ .strategyWorstFit = true };

    /// A bit mask to extract only `STRATEGY` bits from entire set of flags.
    pub const STRATEGY_MASK = AllocationCreateFlags{
        .strategyBestFit = true,
        .strategyWorstFit = true,
        .strategyFirstFit = true,
    };

    pub usingnamespace vk.FlagsMixin(@This(), vk.Flags);
};

pub const AllocationCreateInfo = extern struct {
    /// Use #AllocationCreateFlagBits enum.
    flags: AllocationCreateFlags = .{},
    /// \brief Intended usage of memory.
    ///
    /// You can leave #MemoryUsage.unknown if you specify memory requirements in other way. \n
    /// If `pool` is not null, this member is ignored.
    usage: MemoryUsage = .unknown,
    /// \brief Flags that must be set in a Memory Type chosen for an allocation.
    ///
    /// Leave 0 if you specify memory requirements in other way. \n
    /// If `pool` is not null, this member is ignored.*/
    requiredFlags: vk.MemoryPropertyFlags = .{},
    /// \brief Flags that preferably should be set in a memory type chosen for an allocation.
    ///
    /// Set to 0 if no additional flags are prefered. \n
    /// If `pool` is not null, this member is ignored. */
    preferredFlags: vk.MemoryPropertyFlags = .{},
    /// \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
    ///
    /// Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
    /// it meets other requirements specified by this structure, with no further
    /// restrictions on memory type index. \n
    /// If `pool` is not null, this member is ignored.
    memoryTypeBits: u32 = 0,
    /// \brief Pool that this allocation should be created in.
    ///
    /// Leave `.Null` to allocate from default pool. If not null, members:
    /// `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
    pool: Pool = .Null,
    /// \brief Custom general-purpose pointer that will be stored in #Allocation, can be read as AllocationInfo::pUserData and changed using SetAllocationUserData().
    ///
    /// If #AllocationCreateFlags.userDataCopyString is true, it must be either
    /// null or pointer to a null-terminated string. The string will be then copied to
    /// internal buffer, so it doesn't need to be valid after allocation call.
    pUserData: ?*anyopaque = null,
};

/// Flags to be passed as PoolCreateInfo::flags.
pub const PoolCreateFlags = packed struct {
    __reserved_bit_00: u1 = 0,
    /// \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored.
    ///
    /// This is an optional optimization flag.
    ///
    /// If you always allocate using CreateBuffer(), CreateImage(),
    /// AllocateMemoryForBuffer(), then you don't need to use it because allocator
    /// knows exact type of your allocations so it can handle Buffer-Image Granularity
    /// in the optimal way.
    ///
    /// If you also allocate using AllocateMemoryForImage() or AllocateMemory(),
    /// exact type of such allocations is not known, so allocator must be conservative
    /// in handling Buffer-Image Granularity, which can lead to suboptimal allocation
    /// (wasted memory). In that case, if you can make sure you always allocate only
    /// buffers and linear images or only optimal images out of this pool, use this flag
    /// to make allocator disregard Buffer-Image Granularity and so make allocations
    /// faster and more optimal.
    ignoreBufferImageGranularity: bool = false,

    /// \brief Enables alternative, linear allocation algorithm in this pool.
    ///
    /// Specify this flag to enable linear allocation algorithm, which always creates
    /// new allocations after last one and doesn't reuse space from allocations freed in
    /// between. It trades memory consumption for simplified algorithm and data
    /// structure, which has better performance and uses less memory for metadata.
    ///
    /// By using this flag, you can achieve behavior of free-at-once, stack,
    /// ring buffer, and double stack. For details, see documentation chapter
    /// \ref linear_algorithm.
    ///
    /// When using this flag, you must specify PoolCreateInfo::maxBlockCount == 1 (or 0 for default).
    ///
    /// For more details, see [Linear allocation algorithm](@ref linear_algorithm).
    linearAlgorithm: bool = false,

    /// \brief Enables alternative, buddy allocation algorithm in this pool.
    ///
    /// It operates on a tree of blocks, each having size that is a power of two and
    /// a half of its parent's size. Comparing to default algorithm, this one provides
    /// faster allocation and deallocation and decreased external fragmentation,
    /// at the expense of more memory wasted (internal fragmentation).
    ///
    /// For more details, see [Buddy allocation algorithm](@ref buddy_algorithm).
    buddyAlgorithm: bool = false,

    __reserved_bits_04_31: u28 = 0,

    /// Bit mask to extract only `ALGORITHM` bits from entire set of flags.
    pub const ALGORITHM_MASK = PoolCreateFlags{
        .linearAlgorithm = true,
        .buddyAlgorithm = true,
    };

    pub usingnamespace vk.FlagsMixin(@This());
};

/// \brief Describes parameter of created #Pool.
pub const PoolCreateInfo = extern struct {
    /// \brief Vulkan memory type index to allocate this pool from.
    memoryTypeIndex: u32,
    /// \brief Use combination of #PoolCreateFlagBits.
    flags: PoolCreateFlags = .{},
    /// \brief Size of a single `vk.DeviceMemory` block to be allocated as part of this pool, in bytes. Optional.
    ///
    /// Specify nonzero to set explicit, constant size of memory blocks used by this
    /// pool.
    ///
    /// Leave 0 to use default and let the library manage block sizes automatically.
    /// Sizes of particular blocks may vary.
    blockSize: vk.DeviceSize = 0,
    /// \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty.
    ///
    /// Set to 0 to have no preallocated blocks and allow the pool be completely empty.
    minBlockCount: usize = 0,
    /// \brief Maximum number of blocks that can be allocated in this pool. Optional.
    ///
    /// Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
    ///
    /// Set to same value as PoolCreateInfo::minBlockCount to have fixed amount of memory allocated
    /// throughout whole lifetime of this pool.
    maxBlockCount: usize = 0,
    /// \brief Maximum number of additional frames that are in use at the same time as current frame.
    ///
    /// This value is used only when you make allocations with
    /// #VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT flag. Such allocation cannot become
    /// lost if allocation.lastUseFrameIndex >= allocator.currentFrameIndex - frameInUseCount.
    ///
    /// For example, if you double-buffer your command buffers, so resources used for
    /// rendering in previous frame may still be in use by the GPU at the moment you
    /// allocate resources needed for the current frame, set this value to 1.
    ///
    /// If you want to allow any allocations other than used in the current frame to
    /// become lost, set this value to 0.
    frameInUseCount: u32,
};

/// \brief Describes parameter of existing #Pool.
pub const PoolStats = extern struct {
    /// \brief Total amount of `vk.DeviceMemory` allocated from Vulkan for this pool, in bytes.
    size: vk.DeviceSize,
    /// \brief Total number of bytes in the pool not used by any #Allocation.
    unusedSize: vk.DeviceSize,
    /// \brief Number of #Allocation objects created from this pool that were not destroyed or lost.
    allocationCount: usize,
    /// \brief Number of continuous memory ranges in the pool not used by any #Allocation.
    unusedRangeCount: usize,
    /// \brief Size of the largest continuous free memory region available for new allocation.
    ///
    /// Making a new allocation of that size is not guaranteed to succeed because of
    /// possible additional margin required to respect alignment and buffer/image
    /// granularity.
    unusedRangeSizeMax: vk.DeviceSize,
    /// \brief Number of `vk.DeviceMemory` blocks allocated for this pool.
    blockCount: usize,
};

/// \struct Allocation
/// \brief Represents single memory allocation.
///
/// It may be either dedicated block of `vk.DeviceMemory` or a specific region of a bigger block of this type
/// plus unique offset.
///
/// There are multiple ways to create such object.
/// You need to fill structure AllocationCreateInfo.
/// For more information see [Choosing memory type](@ref choosing_memory_type).
///
/// Although the library provides convenience functions that create Vulkan buffer or image,
/// allocate memory for it and bind them together,
/// binding of the allocation to a buffer or an image is out of scope of the allocation itself.
/// Allocation object can exist without buffer/image bound,
/// binding can be done manually by the user, and destruction of it can be done
/// independently of destruction of the allocation.
///
/// The object also remembers its size and some other information.
/// To retrieve this information, use function GetAllocationInfo() and inspect
/// returned structure AllocationInfo.
///
/// Some kinds allocations can be in lost state.
/// For more information, see [Lost allocations](@ref lost_allocations).
pub const Allocation = enum(usize) { Null = 0, _ };

/// \brief Parameters of #Allocation objects, that can be retrieved using function GetAllocationInfo().
pub const AllocationInfo = extern struct {
    /// \brief Memory type index that this allocation was allocated from.
    ///
    /// It never changes.
    memoryType: u32,
    /// \brief Handle to Vulkan memory object.
    ///
    /// Same memory object can be shared by multiple allocations.
    ///
    /// It can change after call to Defragment() if this allocation is passed to the function, or if allocation is lost.
    ///
    /// If the allocation is lost, it is equal to `.Null`.
    deviceMemory: vk.DeviceMemory,
    /// \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
    ///
    /// It can change after call to Defragment() if this allocation is passed to the function, or if allocation is lost.
    offset: vk.DeviceSize,
    /// \brief Size of this allocation, in bytes.
    ///
    /// It never changes, unless allocation is lost.
    size: vk.DeviceSize,
    /// \brief Pointer to the beginning of this allocation as mapped data.
    ///
    /// If the allocation hasn't been mapped using MapMemory() and hasn't been
    /// created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value null.
    ///
    /// It can change after call to MapMemory(), UnmapMemory().
    /// It can also change after call to Defragment() if this allocation is passed to the function.
    pMappedData: ?*anyopaque,
    /// \brief Custom general-purpose pointer that was passed as AllocationCreateInfo::pUserData or set using SetAllocationUserData().
    ///
    /// It can change after call to SetAllocationUserData() for this allocation.
    pUserData: ?*anyopaque,
};

/// \struct DefragmentationContext
/// \brief Represents Opaque object that represents started defragmentation process.
///
/// Fill structure #DefragmentationInfo2 and call function DefragmentationBegin() to create it.
/// Call function DefragmentationEnd() to destroy it.
pub const DefragmentationContext = enum(usize) { Null = 0, _ };

/// Flags to be used in DefragmentationBegin(). None at the moment. Reserved for future use.
pub const DefragmentationFlags = packed struct {
    __reserved_bits_0_31: u32 = 0,

    pub usingnamespace vk.FlagsMixin(@This());
};

/// \brief Parameters for defragmentation.
///
/// To be used with function DefragmentationBegin().
pub const DefragmentationInfo2 = extern struct {
    /// \brief Reserved for future use. Should be 0.
    flags: DefragmentationFlags = .{},
    /// \brief Number of allocations in `pAllocations` array.
    allocationCount: u32,
    /// \brief Pointer to array of allocations that can be defragmented.
    ///
    /// The array should have `allocationCount` elements.
    /// The array should not contain nulls.
    /// Elements in the array should be unique - same allocation cannot occur twice.
    /// It is safe to pass allocations that are in the lost state - they are ignored.
    /// All allocations not present in this array are considered non-moveable during this defragmentation.
    pAllocations: [*]Allocation,
    /// \brief Optional, output. Pointer to array that will be filled with information whether the allocation at certain index has been changed during defragmentation.
    ///
    /// The array should have `allocationCount` elements.
    /// You can pass null if you are not interested in this information.
    pAllocationsChanged: ?[*]vk.Bool32,
    /// \brief Numer of pools in `pPools` array.
    poolCount: u32,
    /// \brief Either null or pointer to array of pools to be defragmented.
    ///
    /// All the allocations in the specified pools can be moved during defragmentation
    /// and there is no way to check if they were really moved as in `pAllocationsChanged`,
    /// so you must query all the allocations in all these pools for new `vk.DeviceMemory`
    /// and offset using GetAllocationInfo() if you might need to recreate buffers
    /// and images bound to them.
    ///
    /// The array should have `poolCount` elements.
    /// The array should not contain nulls.
    /// Elements in the array should be unique - same pool cannot occur twice.
    ///
    /// Using this array is equivalent to specifying all allocations from the pools in `pAllocations`.
    /// It might be more efficient.
    pPools: ?[*]Pool,
    /// \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on CPU side, like `memcpy()`, `memmove()`.
    ///
    /// `vk.WHOLE_SIZE` means no limit.
    maxCpuBytesToMove: vk.DeviceSize,
    /// \brief Maximum number of allocations that can be moved to a different place using transfers on CPU side, like `memcpy()`, `memmove()`.
    ///
    /// `UINT32_MAX` means no limit.
    maxCpuAllocationsToMove: u32,
    /// \brief Maximum total numbers of bytes that can be copied while moving allocations to different places using transfers on GPU side, posted to `commandBuffer`.
    ///
    /// `vk.WHOLE_SIZE` means no limit.
    maxGpuBytesToMove: vk.DeviceSize,
    /// \brief Maximum number of allocations that can be moved to a different place using transfers on GPU side, posted to `commandBuffer`.
    ///
    /// `UINT32_MAX` means no limit.
    maxGpuAllocationsToMove: u32,
    /// \brief Optional. Command buffer where GPU copy commands will be posted.
    ///
    /// If not null, it must be a valid command buffer handle that supports Transfer queue type.
    /// It must be in the recording state and outside of a render pass instance.
    /// You need to submit it and make sure it finished execution before calling DefragmentationEnd().
    ///
    /// Passing null means that only CPU defragmentation will be performed.
    commandBuffer: vk.CommandBuffer,
};

/// \brief Deprecated. Optional configuration parameters to be passed to function Defragment().
///
/// \deprecated This is a part of the old interface. It is recommended to use structure #DefragmentationInfo2 and function DefragmentationBegin() instead.
pub const DefragmentationInfo = extern struct {
    /// \brief Maximum total numbers of bytes that can be copied while moving allocations to different places.
    ///
    /// Default is `vk.WHOLE_SIZE`, which means no limit.
    maxBytesToMove: vk.DeviceSize,
    /// \brief Maximum number of allocations that can be moved to different place.
    ///
    /// Default is `UINT32_MAX`, which means no limit.
    maxAllocationsToMove: u32,
};

/// \brief Statistics returned by function Defragment().
pub const DefragmentationStats = extern struct {
    /// Total number of bytes that have been copied while moving allocations to different places.
    bytesMoved: vk.DeviceSize,
    /// Total number of bytes that have been released to the system by freeing empty `vk.DeviceMemory` objects.
    bytesFreed: vk.DeviceSize,
    /// Number of allocations that have been moved to different places.
    allocationsMoved: u32,
    /// Number of empty `vk.DeviceMemory` objects that have been released to the system.
    deviceMemoryBlocksFreed: u32,
};

pub extern fn vmaCreateAllocator(pCreateInfo: *const AllocatorCreateInfo, pAllocator: *Allocator) callconv(CallConv) vk.Result;
pub extern fn vmaDestroyAllocator(allocator: Allocator) callconv(CallConv) void;

pub extern fn vmaGetPhysicalDeviceProperties(
    allocator: Allocator,
    ppPhysicalDeviceProperties: **const vk.PhysicalDeviceProperties,
) callconv(CallConv) void;
pub extern fn vmaGetMemoryProperties(
    allocator: Allocator,
    ppPhysicalDeviceMemoryProperties: **const vk.PhysicalDeviceMemoryProperties,
) callconv(CallConv) void;
pub extern fn vmaGetMemoryTypeProperties(
    allocator: Allocator,
    memoryTypeIndex: u32,
    pFlags: *align(4) vk.MemoryPropertyFlags,
) callconv(CallConv) void;

pub extern fn vmaSetCurrentFrameIndex(allocator: Allocator, frameIndex: u32) callconv(CallConv) void;
pub extern fn vmaCalculateStats(allocator: Allocator, pStats: *Stats) callconv(CallConv) void;

pub extern fn vmaGetBudget(
    allocator: Allocator,
    pBudget: *Budget,
) callconv(CallConv) void;

// pub usingnamespace if (config.statsStringEnabled)
//     struct {
//         pub extern fn vmaBuildStatsString(
//             allocator: Allocator,
//             ppStatsString: *[*:0]u8,
//             detailedMap: vk.Bool32,
//         ) callconv(CallConv) void;
//         pub extern fn vmaFreeStatsString(
//             allocator: Allocator,
//             pStatsString: [*:0]u8,
//         ) callconv(CallConv) void;
//     }
// else
//     struct {};

pub extern fn vmaFindMemoryTypeIndex(
    allocator: Allocator,
    memoryTypeBits: u32,
    pAllocationCreateInfo: *const AllocationCreateInfo,
    pMemoryTypeIndex: *u32,
) callconv(CallConv) vk.Result;

pub extern fn vmaFindMemoryTypeIndexForBufferInfo(
    allocator: Allocator,
    pBufferCreateInfo: *const vk.BufferCreateInfo,
    pAllocationCreateInfo: *const AllocationCreateInfo,
    pMemoryTypeIndex: *u32,
) callconv(CallConv) vk.Result;

pub extern fn vmaFindMemoryTypeIndexForImageInfo(
    allocator: Allocator,
    pImageCreateInfo: *const vk.ImageCreateInfo,
    pAllocationCreateInfo: *const AllocationCreateInfo,
    pMemoryTypeIndex: *u32,
) callconv(CallConv) vk.Result;

pub extern fn vmaCreatePool(
    allocator: Allocator,
    pCreateInfo: *const PoolCreateInfo,
    pPool: *Pool,
) callconv(CallConv) vk.Result;

pub extern fn vmaDestroyPool(
    allocator: Allocator,
    pool: Pool,
) callconv(CallConv) void;

pub extern fn vmaGetPoolStats(
    allocator: Allocator,
    pool: Pool,
    pPoolStats: *PoolStats,
) callconv(CallConv) void;

pub extern fn vmaMakePoolAllocationsLost(
    allocator: Allocator,
    pool: Pool,
    pLostAllocationCount: ?*usize,
) callconv(CallConv) void;

pub extern fn vmaCheckPoolCorruption(allocator: Allocator, pool: Pool) callconv(CallConv) vk.Result;

pub extern fn vmaGetPoolName(
    allocator: Allocator,
    pool: Pool,
    ppName: *?[*:0]const u8,
) callconv(CallConv) void;

pub extern fn vmaSetPoolName(
    allocator: Allocator,
    pool: Pool,
    pName: ?[*:0]const u8,
) callconv(CallConv) void;

pub extern fn vmaAllocateMemory(
    allocator: Allocator,
    pVkMemoryRequirements: *const vk.MemoryRequirements,
    pCreateInfo: *const AllocationCreateInfo,
    pAllocation: *Allocation,
    pAllocationInfo: ?*AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaAllocateMemoryPages(
    allocator: Allocator,
    pVkMemoryRequirements: *const vk.MemoryRequirements,
    pCreateInfo: *const AllocationCreateInfo,
    allocationCount: usize,
    pAllocations: [*]Allocation,
    pAllocationInfo: ?[*]AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaAllocateMemoryForBuffer(
    allocator: Allocator,
    buffer: vk.Buffer,
    pCreateInfo: *const AllocationCreateInfo,
    pAllocation: *Allocation,
    pAllocationInfo: ?*AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaAllocateMemoryForImage(
    allocator: Allocator,
    image: vk.Image,
    pCreateInfo: *const AllocationCreateInfo,
    pAllocation: *Allocation,
    pAllocationInfo: ?*AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaFreeMemory(
    allocator: Allocator,
    allocation: Allocation,
) callconv(CallConv) void;

pub extern fn vmaFreeMemoryPages(
    allocator: Allocator,
    allocationCount: usize,
    pAllocations: [*]Allocation,
) callconv(CallConv) void;

/// \brief Deprecated.
///
/// In version 2.2.0 it used to try to change allocation's size without moving or reallocating it.
/// In current version it returns `vk.SUCCESS` only if `newSize` equals current allocation's size.
/// Otherwise returns `error.VK_OUT_OF_POOL_MEMORY`, indicating that allocation's size could not be changed.
pub extern fn vmaResizeAllocation(
    allocator: Allocator,
    allocation: Allocation,
    newSize: vk.DeviceSize,
) callconv(CallConv) vk.Result;

pub extern fn vmaGetAllocationInfo(
    allocator: Allocator,
    allocation: Allocation,
    pAllocationInfo: *AllocationInfo,
) callconv(CallConv) void;

pub extern fn vmaTouchAllocation(
    allocator: Allocator,
    allocation: Allocation,
) callconv(CallConv) vk.Bool32;

pub extern fn vmaSetAllocationUserData(
    allocator: Allocator,
    allocation: Allocation,
    pUserData: ?*anyopaque,
) callconv(CallConv) void;

pub extern fn vmaCreateLostAllocation(
    allocator: Allocator,
    pAllocation: *Allocation,
) callconv(CallConv) void;

pub extern fn vmaMapMemory(
    allocator: Allocator,
    allocation: Allocation,
    ppData: **anyopaque,
) callconv(CallConv) vk.Result;

pub extern fn vmaUnmapMemory(
    allocator: Allocator,
    allocation: Allocation,
) callconv(CallConv) void;

pub extern fn vmaFlushAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) callconv(CallConv) void;

pub extern fn vmaInvalidateAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) callconv(CallConv) void;

pub extern fn vmaCheckCorruption(allocator: Allocator, memoryTypeBits: u32) callconv(CallConv) vk.Result;

pub extern fn vmaDefragmentationBegin(
    allocator: Allocator,
    pInfo: *const DefragmentationInfo2,
    pStats: ?*DefragmentationStats,
    pContext: *DefragmentationContext,
) callconv(CallConv) vk.Result;

pub extern fn vmaDefragmentationEnd(
    allocator: Allocator,
    context: DefragmentationContext,
) callconv(CallConv) vk.Result;

/// \brief Deprecated. Compacts memory by moving allocations.
///
/// @param pAllocations Array of allocations that can be moved during this compation.
/// @param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays.
/// @param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
/// @param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values.
/// @param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information.
/// @return `vk.SUCCESS` if completed, negative error code in case of error.
///
/// \deprecated This is a part of the old interface. It is recommended to use structure #DefragmentationInfo2 and function DefragmentationBegin() instead.
///
/// This function works by moving allocations to different places (different
/// `vk.DeviceMemory` objects and/or different offsets) in order to optimize memory
/// usage. Only allocations that are in `pAllocations` array can be moved. All other
/// allocations are considered nonmovable in this call. Basic rules:
///
/// - Only allocations made in memory types that have
/// `vk.MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `vk.MEMORY_PROPERTY_HOST_COHERENT_BIT`
/// flags can be compacted. You may pass other allocations but it makes no sense -
/// these will never be moved.
/// - Custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT or
/// #VMA_POOL_CREATE_BUDDY_ALGORITHM_BIT flag are not defragmented. Allocations
/// passed to this function that come from such pools are ignored.
/// - Allocations created with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT or
/// created as dedicated allocations for any other reason are also ignored.
/// - Both allocations made with or without #VMA_ALLOCATION_CREATE_MAPPED_BIT
/// flag can be compacted. If not persistently mapped, memory will be mapped
/// temporarily inside this function if needed.
/// - You must not pass same #Allocation object multiple times in `pAllocations` array.
///
/// The function also frees empty `vk.DeviceMemory` blocks.
///
/// Warning: This function may be time-consuming, so you shouldn't call it too often
/// (like after every resource creation/destruction).
/// You can call it on special occasions (like when reloading a game level or
/// when you just destroyed a lot of objects). Calling it every frame may be OK, but
/// you should measure that on your platform.
///
/// For more information, see [Defragmentation](@ref defragmentation) chapter.
pub extern fn vmaDefragment(
    allocator: Allocator,
    pAllocations: *Allocation,
    allocationCount: usize,
    pAllocationsChanged: *vk.Bool32,
    pDefragmentationInfo: *const DefragmentationInfo,
    pDefragmentationStats: *DefragmentationStats,
) callconv(CallConv) vk.Result;

pub extern fn vmaBindBufferMemory(
    allocator: Allocator,
    allocation: Allocation,
    buffer: vk.Buffer,
) callconv(CallConv) vk.Result;

pub extern fn vmaBindBufferMemory2(
    allocator: Allocator,
    allocation: Allocation,
    allocationLocalOffset: vk.DeviceSize,
    buffer: vk.Buffer,
    pNext: ?*const anyopaque,
) callconv(CallConv) vk.Result;

pub extern fn vmaBindImageMemory(
    allocator: Allocator,
    allocation: Allocation,
    image: vk.Image,
) callconv(CallConv) vk.Result;

pub extern fn vmaBindImageMemory2(
    allocator: Allocator,
    allocation: Allocation,
    allocationLocalOffset: vk.DeviceSize,
    image: vk.Image,
    pNext: ?*const anyopaque,
) callconv(CallConv) vk.Result;

pub extern fn vmaCreateBuffer(
    allocator: Allocator,
    pBufferCreateInfo: *const vk.BufferCreateInfo,
    pAllocationCreateInfo: *const AllocationCreateInfo,
    pBuffer: *vk.Buffer,
    pAllocation: *Allocation,
    pAllocationInfo: ?*AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaDestroyBuffer(
    allocator: Allocator,
    buffer: vk.Buffer,
    allocation: Allocation,
) callconv(CallConv) void;

pub extern fn vmaCreateImage(
    allocator: Allocator,
    pImageCreateInfo: *const vk.ImageCreateInfo,
    pAllocationCreateInfo: *const AllocationCreateInfo,
    pImage: *vk.Image,
    pAllocation: *Allocation,
    pAllocationInfo: ?*AllocationInfo,
) callconv(CallConv) vk.Result;

pub extern fn vmaDestroyImage(
    allocator: Allocator,
    image: vk.Image,
    allocation: Allocation,
) callconv(CallConv) void;
