2098 lines
103 KiB
Zig
2098 lines
103 KiB
Zig
// 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;
|