// 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;