packages feed

VulkanMemoryAllocator 0.11.0.1 → 0.11.1.0

raw patch · 6 files changed

+19457/−19557 lines, 6 filesdep ~vulkansetup-changednew-uploaderPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: vulkan

API changes (from Hackage documentation)

- VulkanMemoryAllocator: [$sel:alignment:VirtualAllocationCreateInfo] :: VirtualAllocationCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:allocationBytes:Statistics] :: Statistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:allocationCallbacks:AllocatorCreateInfo] :: AllocatorCreateInfo -> Maybe AllocationCallbacks
- VulkanMemoryAllocator: [$sel:allocationCallbacks:VirtualBlockCreateInfo] :: VirtualBlockCreateInfo -> Maybe AllocationCallbacks
- VulkanMemoryAllocator: [$sel:allocationCount:Statistics] :: Statistics -> Word32
- VulkanMemoryAllocator: [$sel:allocationSizeMax:DetailedStatistics] :: DetailedStatistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:allocationSizeMin:DetailedStatistics] :: DetailedStatistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:allocationsMoved:DefragmentationStats] :: DefragmentationStats -> Word32
- VulkanMemoryAllocator: [$sel:blockBytes:Statistics] :: Statistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:blockCount:Statistics] :: Statistics -> Word32
- VulkanMemoryAllocator: [$sel:blockSize:PoolCreateInfo] :: PoolCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:breakCallbackUserData:DefragmentationInfo] :: DefragmentationInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:budget:Budget] :: Budget -> DeviceSize
- VulkanMemoryAllocator: [$sel:bytesFreed:DefragmentationStats] :: DefragmentationStats -> DeviceSize
- VulkanMemoryAllocator: [$sel:bytesMoved:DefragmentationStats] :: DefragmentationStats -> DeviceSize
- VulkanMemoryAllocator: [$sel:device:AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr Device_T
- VulkanMemoryAllocator: [$sel:device:AllocatorInfo] :: AllocatorInfo -> Ptr Device_T
- VulkanMemoryAllocator: [$sel:deviceMemory:AllocationInfo] :: AllocationInfo -> DeviceMemory
- VulkanMemoryAllocator: [$sel:deviceMemoryBlocksFreed:DefragmentationStats] :: DefragmentationStats -> Word32
- VulkanMemoryAllocator: [$sel:deviceMemoryCallbacks:AllocatorCreateInfo] :: AllocatorCreateInfo -> Maybe DeviceMemoryCallbacks
- VulkanMemoryAllocator: [$sel:dstTmpAllocation:DefragmentationMove] :: DefragmentationMove -> Allocation
- VulkanMemoryAllocator: [$sel:flags:AllocationCreateInfo] :: AllocationCreateInfo -> AllocationCreateFlags
- VulkanMemoryAllocator: [$sel:flags:AllocatorCreateInfo] :: AllocatorCreateInfo -> AllocatorCreateFlags
- VulkanMemoryAllocator: [$sel:flags:DefragmentationInfo] :: DefragmentationInfo -> DefragmentationFlags
- VulkanMemoryAllocator: [$sel:flags:PoolCreateInfo] :: PoolCreateInfo -> PoolCreateFlags
- VulkanMemoryAllocator: [$sel:flags:VirtualAllocationCreateInfo] :: VirtualAllocationCreateInfo -> VirtualAllocationCreateFlags
- VulkanMemoryAllocator: [$sel:flags:VirtualBlockCreateInfo] :: VirtualBlockCreateInfo -> VirtualBlockCreateFlags
- VulkanMemoryAllocator: [$sel:heapSizeLimit:AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr DeviceSize
- VulkanMemoryAllocator: [$sel:instance':AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr Instance_T
- VulkanMemoryAllocator: [$sel:instance':AllocatorInfo] :: AllocatorInfo -> Ptr Instance_T
- VulkanMemoryAllocator: [$sel:mappedData:AllocationInfo] :: AllocationInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:maxAllocationsPerPass:DefragmentationInfo] :: DefragmentationInfo -> Word32
- VulkanMemoryAllocator: [$sel:maxBlockCount:PoolCreateInfo] :: PoolCreateInfo -> Word64
- VulkanMemoryAllocator: [$sel:maxBytesPerPass:DefragmentationInfo] :: DefragmentationInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:memoryAllocateNext:PoolCreateInfo] :: PoolCreateInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:memoryHeap:TotalStatistics] :: TotalStatistics -> Vector DetailedStatistics
- VulkanMemoryAllocator: [$sel:memoryType:AllocationInfo] :: AllocationInfo -> Word32
- VulkanMemoryAllocator: [$sel:memoryType:TotalStatistics] :: TotalStatistics -> Vector DetailedStatistics
- VulkanMemoryAllocator: [$sel:memoryTypeBits:AllocationCreateInfo] :: AllocationCreateInfo -> Word32
- VulkanMemoryAllocator: [$sel:memoryTypeIndex:PoolCreateInfo] :: PoolCreateInfo -> Word32
- VulkanMemoryAllocator: [$sel:minAllocationAlignment:PoolCreateInfo] :: PoolCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:minBlockCount:PoolCreateInfo] :: PoolCreateInfo -> Word64
- VulkanMemoryAllocator: [$sel:moveCount:DefragmentationPassMoveInfo] :: DefragmentationPassMoveInfo -> Word32
- VulkanMemoryAllocator: [$sel:moves:DefragmentationPassMoveInfo] :: DefragmentationPassMoveInfo -> Ptr DefragmentationMove
- VulkanMemoryAllocator: [$sel:name:AllocationInfo] :: AllocationInfo -> Maybe ByteString
- VulkanMemoryAllocator: [$sel:offset:AllocationInfo] :: AllocationInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:offset:VirtualAllocationInfo] :: VirtualAllocationInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:operation:DefragmentationMove] :: DefragmentationMove -> DefragmentationMoveOperation
- VulkanMemoryAllocator: [$sel:pfnAllocate:DeviceMemoryCallbacks] :: DeviceMemoryCallbacks -> PFN_vmaAllocateDeviceMemoryFunction
- VulkanMemoryAllocator: [$sel:pfnBreakCallback:DefragmentationInfo] :: DefragmentationInfo -> PFN_vmaCheckDefragmentationBreakFunction
- VulkanMemoryAllocator: [$sel:pfnFree:DeviceMemoryCallbacks] :: DeviceMemoryCallbacks -> PFN_vmaFreeDeviceMemoryFunction
- VulkanMemoryAllocator: [$sel:physicalDevice:AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr PhysicalDevice_T
- VulkanMemoryAllocator: [$sel:physicalDevice:AllocatorInfo] :: AllocatorInfo -> Ptr PhysicalDevice_T
- VulkanMemoryAllocator: [$sel:pool:AllocationCreateInfo] :: AllocationCreateInfo -> Pool
- VulkanMemoryAllocator: [$sel:pool:DefragmentationInfo] :: DefragmentationInfo -> Pool
- VulkanMemoryAllocator: [$sel:preferredFlags:AllocationCreateInfo] :: AllocationCreateInfo -> MemoryPropertyFlags
- VulkanMemoryAllocator: [$sel:preferredLargeHeapBlockSize:AllocatorCreateInfo] :: AllocatorCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:priority:AllocationCreateInfo] :: AllocationCreateInfo -> Float
- VulkanMemoryAllocator: [$sel:priority:PoolCreateInfo] :: PoolCreateInfo -> Float
- VulkanMemoryAllocator: [$sel:requiredFlags:AllocationCreateInfo] :: AllocationCreateInfo -> MemoryPropertyFlags
- VulkanMemoryAllocator: [$sel:size:AllocationInfo] :: AllocationInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:size:VirtualAllocationCreateInfo] :: VirtualAllocationCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:size:VirtualAllocationInfo] :: VirtualAllocationInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:size:VirtualBlockCreateInfo] :: VirtualBlockCreateInfo -> DeviceSize
- VulkanMemoryAllocator: [$sel:srcAllocation:DefragmentationMove] :: DefragmentationMove -> Allocation
- VulkanMemoryAllocator: [$sel:statistics:Budget] :: Budget -> Statistics
- VulkanMemoryAllocator: [$sel:statistics:DetailedStatistics] :: DetailedStatistics -> Statistics
- VulkanMemoryAllocator: [$sel:total:TotalStatistics] :: TotalStatistics -> DetailedStatistics
- VulkanMemoryAllocator: [$sel:typeExternalMemoryHandleTypes:AllocatorCreateInfo] :: AllocatorCreateInfo -> Ptr ExternalMemoryHandleTypeFlagsKHR
- VulkanMemoryAllocator: [$sel:unusedRangeCount:DetailedStatistics] :: DetailedStatistics -> Word32
- VulkanMemoryAllocator: [$sel:unusedRangeSizeMax:DetailedStatistics] :: DetailedStatistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:unusedRangeSizeMin:DetailedStatistics] :: DetailedStatistics -> DeviceSize
- VulkanMemoryAllocator: [$sel:usage:AllocationCreateInfo] :: AllocationCreateInfo -> MemoryUsage
- VulkanMemoryAllocator: [$sel:usage:Budget] :: Budget -> DeviceSize
- VulkanMemoryAllocator: [$sel:userData:AllocationCreateInfo] :: AllocationCreateInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:userData:AllocationInfo] :: AllocationInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:userData:DeviceMemoryCallbacks] :: DeviceMemoryCallbacks -> Ptr ()
- VulkanMemoryAllocator: [$sel:userData:VirtualAllocationCreateInfo] :: VirtualAllocationCreateInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:userData:VirtualAllocationInfo] :: VirtualAllocationInfo -> Ptr ()
- VulkanMemoryAllocator: [$sel:vkAllocateMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkAllocateMemory
- VulkanMemoryAllocator: [$sel:vkBindBufferMemory2KHR:VulkanFunctions] :: VulkanFunctions -> PFN_vkBindBufferMemory2KHR
- VulkanMemoryAllocator: [$sel:vkBindBufferMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkBindBufferMemory
- VulkanMemoryAllocator: [$sel:vkBindImageMemory2KHR:VulkanFunctions] :: VulkanFunctions -> PFN_vkBindImageMemory2KHR
- VulkanMemoryAllocator: [$sel:vkBindImageMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkBindImageMemory
- VulkanMemoryAllocator: [$sel:vkCmdCopyBuffer:VulkanFunctions] :: VulkanFunctions -> PFN_vkCmdCopyBuffer
- VulkanMemoryAllocator: [$sel:vkCreateBuffer:VulkanFunctions] :: VulkanFunctions -> PFN_vkCreateBuffer
- VulkanMemoryAllocator: [$sel:vkCreateImage:VulkanFunctions] :: VulkanFunctions -> PFN_vkCreateImage
- VulkanMemoryAllocator: [$sel:vkDestroyBuffer:VulkanFunctions] :: VulkanFunctions -> PFN_vkDestroyBuffer
- VulkanMemoryAllocator: [$sel:vkDestroyImage:VulkanFunctions] :: VulkanFunctions -> PFN_vkDestroyImage
- VulkanMemoryAllocator: [$sel:vkFlushMappedMemoryRanges:VulkanFunctions] :: VulkanFunctions -> PFN_vkFlushMappedMemoryRanges
- VulkanMemoryAllocator: [$sel:vkFreeMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkFreeMemory
- VulkanMemoryAllocator: [$sel:vkGetBufferMemoryRequirements2KHR:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetBufferMemoryRequirements2KHR
- VulkanMemoryAllocator: [$sel:vkGetBufferMemoryRequirements:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetBufferMemoryRequirements
- VulkanMemoryAllocator: [$sel:vkGetDeviceBufferMemoryRequirements:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetDeviceBufferMemoryRequirements
- VulkanMemoryAllocator: [$sel:vkGetDeviceImageMemoryRequirements:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetDeviceImageMemoryRequirements
- VulkanMemoryAllocator: [$sel:vkGetDeviceProcAddr:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetDeviceProcAddr
- VulkanMemoryAllocator: [$sel:vkGetImageMemoryRequirements2KHR:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetImageMemoryRequirements2KHR
- VulkanMemoryAllocator: [$sel:vkGetImageMemoryRequirements:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetImageMemoryRequirements
- VulkanMemoryAllocator: [$sel:vkGetInstanceProcAddr:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetInstanceProcAddr
- VulkanMemoryAllocator: [$sel:vkGetPhysicalDeviceMemoryProperties2KHR:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceMemoryProperties2KHR
- VulkanMemoryAllocator: [$sel:vkGetPhysicalDeviceMemoryProperties:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceMemoryProperties
- VulkanMemoryAllocator: [$sel:vkGetPhysicalDeviceProperties:VulkanFunctions] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceProperties
- VulkanMemoryAllocator: [$sel:vkInvalidateMappedMemoryRanges:VulkanFunctions] :: VulkanFunctions -> PFN_vkInvalidateMappedMemoryRanges
- VulkanMemoryAllocator: [$sel:vkMapMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkMapMemory
- VulkanMemoryAllocator: [$sel:vkUnmapMemory:VulkanFunctions] :: VulkanFunctions -> PFN_vkUnmapMemory
- VulkanMemoryAllocator: [$sel:vulkanApiVersion:AllocatorCreateInfo] :: AllocatorCreateInfo -> Word32
- VulkanMemoryAllocator: [$sel:vulkanFunctions:AllocatorCreateInfo] :: AllocatorCreateInfo -> Maybe VulkanFunctions
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.Allocation
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.AllocationCreateFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.AllocationCreateInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.Allocator
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.AllocatorCreateFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.AllocatorInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.Budget
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationContext
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationMove
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationMoveOperation
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationPassMoveInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationStats
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DetailedStatistics
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.DeviceMemoryCallbacks
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.MemoryUsage
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.Pool
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.PoolCreateFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.PoolCreateInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.Statistics
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.TotalStatistics
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocation
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationCreateInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationInfo
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualBlock
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VirtualBlockCreateFlagBits
- VulkanMemoryAllocator: instance Foreign.Storable.Storable VulkanMemoryAllocator.VulkanFunctions
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.AllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.AllocatorCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.DefragmentationFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.PoolCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.Bits VulkanMemoryAllocator.VirtualBlockCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.AllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.AllocatorCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.DefragmentationFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.PoolCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Bits.FiniteBits VulkanMemoryAllocator.VirtualBlockCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.AllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.AllocatorCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.DefragmentationFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.DefragmentationMoveOperation
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.MemoryUsage
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.PoolCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Read.Read VulkanMemoryAllocator.VirtualBlockCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.Allocation
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocationCreateInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocationInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.Allocator
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocatorCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocatorCreateInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.AllocatorInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.Budget
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationContext
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationMove
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationMoveOperation
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationPassMoveInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DefragmentationStats
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DetailedStatistics
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.DeviceMemoryCallbacks
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.MemoryUsage
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.Pool
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.PoolCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.PoolCreateInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.Statistics
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.TotalStatistics
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualAllocation
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualAllocationCreateInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualAllocationInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualBlock
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualBlockCreateFlagBits
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VirtualBlockCreateInfo
- VulkanMemoryAllocator: instance GHC.Show.Show VulkanMemoryAllocator.VulkanFunctions
+ VulkanMemoryAllocator: [alignment] :: VirtualAllocationCreateInfo -> DeviceSize
+ VulkanMemoryAllocator: [allocationBytes] :: Statistics -> DeviceSize
+ VulkanMemoryAllocator: [allocationCallbacks] :: VirtualBlockCreateInfo -> Maybe AllocationCallbacks
+ VulkanMemoryAllocator: [allocationCount] :: Statistics -> Word32
+ VulkanMemoryAllocator: [allocationSizeMax] :: DetailedStatistics -> DeviceSize
+ VulkanMemoryAllocator: [allocationSizeMin] :: DetailedStatistics -> DeviceSize
+ VulkanMemoryAllocator: [allocationsMoved] :: DefragmentationStats -> Word32
+ VulkanMemoryAllocator: [blockBytes] :: Statistics -> DeviceSize
+ VulkanMemoryAllocator: [blockCount] :: Statistics -> Word32
+ VulkanMemoryAllocator: [blockSize] :: PoolCreateInfo -> DeviceSize
+ VulkanMemoryAllocator: [breakCallbackUserData] :: DefragmentationInfo -> Ptr ()
+ VulkanMemoryAllocator: [budget] :: Budget -> DeviceSize
+ VulkanMemoryAllocator: [bytesFreed] :: DefragmentationStats -> DeviceSize
+ VulkanMemoryAllocator: [bytesMoved] :: DefragmentationStats -> DeviceSize
+ VulkanMemoryAllocator: [deviceMemoryBlocksFreed] :: DefragmentationStats -> Word32
+ VulkanMemoryAllocator: [deviceMemoryCallbacks] :: AllocatorCreateInfo -> Maybe DeviceMemoryCallbacks
+ VulkanMemoryAllocator: [deviceMemory] :: AllocationInfo -> DeviceMemory
+ VulkanMemoryAllocator: [device] :: AllocatorInfo -> Ptr Device_T
+ VulkanMemoryAllocator: [dstTmpAllocation] :: DefragmentationMove -> Allocation
+ VulkanMemoryAllocator: [flags] :: VirtualAllocationCreateInfo -> VirtualAllocationCreateFlags
+ VulkanMemoryAllocator: [heapSizeLimit] :: AllocatorCreateInfo -> Ptr DeviceSize
+ VulkanMemoryAllocator: [instance'] :: AllocatorInfo -> Ptr Instance_T
+ VulkanMemoryAllocator: [mappedData] :: AllocationInfo -> Ptr ()
+ VulkanMemoryAllocator: [maxAllocationsPerPass] :: DefragmentationInfo -> Word32
+ VulkanMemoryAllocator: [maxBlockCount] :: PoolCreateInfo -> Word64
+ VulkanMemoryAllocator: [maxBytesPerPass] :: DefragmentationInfo -> DeviceSize
+ VulkanMemoryAllocator: [memoryAllocateNext] :: PoolCreateInfo -> Ptr ()
+ VulkanMemoryAllocator: [memoryHeap] :: TotalStatistics -> Vector DetailedStatistics
+ VulkanMemoryAllocator: [memoryTypeBits] :: AllocationCreateInfo -> Word32
+ VulkanMemoryAllocator: [memoryTypeIndex] :: PoolCreateInfo -> Word32
+ VulkanMemoryAllocator: [memoryType] :: AllocationInfo -> Word32
+ VulkanMemoryAllocator: [minAllocationAlignment] :: PoolCreateInfo -> DeviceSize
+ VulkanMemoryAllocator: [minBlockCount] :: PoolCreateInfo -> Word64
+ VulkanMemoryAllocator: [moveCount] :: DefragmentationPassMoveInfo -> Word32
+ VulkanMemoryAllocator: [moves] :: DefragmentationPassMoveInfo -> Ptr DefragmentationMove
+ VulkanMemoryAllocator: [name] :: AllocationInfo -> Maybe ByteString
+ VulkanMemoryAllocator: [offset] :: VirtualAllocationInfo -> DeviceSize
+ VulkanMemoryAllocator: [operation] :: DefragmentationMove -> DefragmentationMoveOperation
+ VulkanMemoryAllocator: [pfnAllocate] :: DeviceMemoryCallbacks -> PFN_vmaAllocateDeviceMemoryFunction
+ VulkanMemoryAllocator: [pfnBreakCallback] :: DefragmentationInfo -> PFN_vmaCheckDefragmentationBreakFunction
+ VulkanMemoryAllocator: [pfnFree] :: DeviceMemoryCallbacks -> PFN_vmaFreeDeviceMemoryFunction
+ VulkanMemoryAllocator: [physicalDevice] :: AllocatorInfo -> Ptr PhysicalDevice_T
+ VulkanMemoryAllocator: [pool] :: DefragmentationInfo -> Pool
+ VulkanMemoryAllocator: [preferredFlags] :: AllocationCreateInfo -> MemoryPropertyFlags
+ VulkanMemoryAllocator: [preferredLargeHeapBlockSize] :: AllocatorCreateInfo -> DeviceSize
+ VulkanMemoryAllocator: [priority] :: PoolCreateInfo -> Float
+ VulkanMemoryAllocator: [requiredFlags] :: AllocationCreateInfo -> MemoryPropertyFlags
+ VulkanMemoryAllocator: [size] :: VirtualAllocationInfo -> DeviceSize
+ VulkanMemoryAllocator: [srcAllocation] :: DefragmentationMove -> Allocation
+ VulkanMemoryAllocator: [statistics] :: Budget -> Statistics
+ VulkanMemoryAllocator: [total] :: TotalStatistics -> DetailedStatistics
+ VulkanMemoryAllocator: [typeExternalMemoryHandleTypes] :: AllocatorCreateInfo -> Ptr ExternalMemoryHandleTypeFlagsKHR
+ VulkanMemoryAllocator: [unusedRangeCount] :: DetailedStatistics -> Word32
+ VulkanMemoryAllocator: [unusedRangeSizeMax] :: DetailedStatistics -> DeviceSize
+ VulkanMemoryAllocator: [unusedRangeSizeMin] :: DetailedStatistics -> DeviceSize
+ VulkanMemoryAllocator: [usage] :: AllocationCreateInfo -> MemoryUsage
+ VulkanMemoryAllocator: [userData] :: VirtualAllocationInfo -> Ptr ()
+ VulkanMemoryAllocator: [vkAllocateMemory] :: VulkanFunctions -> PFN_vkAllocateMemory
+ VulkanMemoryAllocator: [vkBindBufferMemory2KHR] :: VulkanFunctions -> PFN_vkBindBufferMemory2KHR
+ VulkanMemoryAllocator: [vkBindBufferMemory] :: VulkanFunctions -> PFN_vkBindBufferMemory
+ VulkanMemoryAllocator: [vkBindImageMemory2KHR] :: VulkanFunctions -> PFN_vkBindImageMemory2KHR
+ VulkanMemoryAllocator: [vkBindImageMemory] :: VulkanFunctions -> PFN_vkBindImageMemory
+ VulkanMemoryAllocator: [vkCmdCopyBuffer] :: VulkanFunctions -> PFN_vkCmdCopyBuffer
+ VulkanMemoryAllocator: [vkCreateBuffer] :: VulkanFunctions -> PFN_vkCreateBuffer
+ VulkanMemoryAllocator: [vkCreateImage] :: VulkanFunctions -> PFN_vkCreateImage
+ VulkanMemoryAllocator: [vkDestroyBuffer] :: VulkanFunctions -> PFN_vkDestroyBuffer
+ VulkanMemoryAllocator: [vkDestroyImage] :: VulkanFunctions -> PFN_vkDestroyImage
+ VulkanMemoryAllocator: [vkFlushMappedMemoryRanges] :: VulkanFunctions -> PFN_vkFlushMappedMemoryRanges
+ VulkanMemoryAllocator: [vkFreeMemory] :: VulkanFunctions -> PFN_vkFreeMemory
+ VulkanMemoryAllocator: [vkGetBufferMemoryRequirements2KHR] :: VulkanFunctions -> PFN_vkGetBufferMemoryRequirements2KHR
+ VulkanMemoryAllocator: [vkGetBufferMemoryRequirements] :: VulkanFunctions -> PFN_vkGetBufferMemoryRequirements
+ VulkanMemoryAllocator: [vkGetDeviceBufferMemoryRequirements] :: VulkanFunctions -> PFN_vkGetDeviceBufferMemoryRequirements
+ VulkanMemoryAllocator: [vkGetDeviceImageMemoryRequirements] :: VulkanFunctions -> PFN_vkGetDeviceImageMemoryRequirements
+ VulkanMemoryAllocator: [vkGetDeviceProcAddr] :: VulkanFunctions -> PFN_vkGetDeviceProcAddr
+ VulkanMemoryAllocator: [vkGetImageMemoryRequirements2KHR] :: VulkanFunctions -> PFN_vkGetImageMemoryRequirements2KHR
+ VulkanMemoryAllocator: [vkGetImageMemoryRequirements] :: VulkanFunctions -> PFN_vkGetImageMemoryRequirements
+ VulkanMemoryAllocator: [vkGetInstanceProcAddr] :: VulkanFunctions -> PFN_vkGetInstanceProcAddr
+ VulkanMemoryAllocator: [vkGetPhysicalDeviceMemoryProperties2KHR] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceMemoryProperties2KHR
+ VulkanMemoryAllocator: [vkGetPhysicalDeviceMemoryProperties] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceMemoryProperties
+ VulkanMemoryAllocator: [vkGetPhysicalDeviceProperties] :: VulkanFunctions -> PFN_vkGetPhysicalDeviceProperties
+ VulkanMemoryAllocator: [vkInvalidateMappedMemoryRanges] :: VulkanFunctions -> PFN_vkInvalidateMappedMemoryRanges
+ VulkanMemoryAllocator: [vkMapMemory] :: VulkanFunctions -> PFN_vkMapMemory
+ VulkanMemoryAllocator: [vkUnmapMemory] :: VulkanFunctions -> PFN_vkUnmapMemory
+ VulkanMemoryAllocator: [vulkanApiVersion] :: AllocatorCreateInfo -> Word32
+ VulkanMemoryAllocator: [vulkanFunctions] :: AllocatorCreateInfo -> Maybe VulkanFunctions
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.AllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.AllocatorCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.DefragmentationFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.PoolCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.Bits VulkanMemoryAllocator.VirtualBlockCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.AllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.AllocatorCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.DefragmentationFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.PoolCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Bits.FiniteBits VulkanMemoryAllocator.VirtualBlockCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.Allocation
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.AllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.AllocationCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.Allocator
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.AllocatorCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.AllocatorInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.Budget
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationContext
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationMove
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationMoveOperation
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationPassMoveInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DefragmentationStats
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DetailedStatistics
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.DeviceMemoryCallbacks
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.MemoryUsage
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.Pool
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.PoolCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.PoolCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.Statistics
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.TotalStatistics
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocation
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualAllocationInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualBlock
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VirtualBlockCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Foreign.Storable.Storable VulkanMemoryAllocator.VulkanFunctions
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.AllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.AllocatorCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.DefragmentationFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.DefragmentationMoveOperation
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.MemoryUsage
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.PoolCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Read.Read VulkanMemoryAllocator.VirtualBlockCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.Allocation
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocationCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocationInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.Allocator
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocatorCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocatorCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.AllocatorInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.Budget
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationContext
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationMove
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationMoveOperation
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationPassMoveInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DefragmentationStats
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DetailedStatistics
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.DeviceMemoryCallbacks
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.MemoryUsage
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.Pool
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.PoolCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.PoolCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.Statistics
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.TotalStatistics
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualAllocation
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualAllocationCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualAllocationCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualAllocationInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualBlock
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualBlockCreateFlagBits
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VirtualBlockCreateInfo
+ VulkanMemoryAllocator: instance GHC.Internal.Show.Show VulkanMemoryAllocator.VulkanFunctions
+ VulkanMemoryAllocator.Utils: allocatorCreateInfo :: AllocatorCreateFlags -> Word32 -> Instance -> PhysicalDevice -> Device -> AllocatorCreateInfo
- VulkanMemoryAllocator: allocateMemory :: forall io. MonadIO io => Allocator -> ("vkMemoryRequirements" ::: MemoryRequirements) -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
+ VulkanMemoryAllocator: allocateMemory :: MonadIO io => Allocator -> ("vkMemoryRequirements" ::: MemoryRequirements) -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
- VulkanMemoryAllocator: allocateMemoryForBuffer :: forall io. MonadIO io => Allocator -> Buffer -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
+ VulkanMemoryAllocator: allocateMemoryForBuffer :: MonadIO io => Allocator -> Buffer -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
- VulkanMemoryAllocator: allocateMemoryForImage :: forall io. MonadIO io => Allocator -> Image -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
+ VulkanMemoryAllocator: allocateMemoryForImage :: MonadIO io => Allocator -> Image -> AllocationCreateInfo -> io (Allocation, AllocationInfo)
- VulkanMemoryAllocator: allocateMemoryPages :: forall io. MonadIO io => Allocator -> ("vkMemoryRequirements" ::: Vector MemoryRequirements) -> ("createInfo" ::: Vector AllocationCreateInfo) -> io ("allocations" ::: Vector Allocation, "allocationInfo" ::: Vector AllocationInfo)
+ VulkanMemoryAllocator: allocateMemoryPages :: MonadIO io => Allocator -> ("vkMemoryRequirements" ::: Vector MemoryRequirements) -> ("createInfo" ::: Vector AllocationCreateInfo) -> io ("allocations" ::: Vector Allocation, "allocationInfo" ::: Vector AllocationInfo)
- VulkanMemoryAllocator: beginDefragmentation :: forall io. MonadIO io => Allocator -> DefragmentationInfo -> io DefragmentationContext
+ VulkanMemoryAllocator: beginDefragmentation :: MonadIO io => Allocator -> DefragmentationInfo -> io DefragmentationContext
- VulkanMemoryAllocator: beginDefragmentationPass :: forall io. MonadIO io => Allocator -> DefragmentationContext -> io ("passInfo" ::: DefragmentationPassMoveInfo)
+ VulkanMemoryAllocator: beginDefragmentationPass :: MonadIO io => Allocator -> DefragmentationContext -> io ("passInfo" ::: DefragmentationPassMoveInfo)
- VulkanMemoryAllocator: bindBufferMemory :: forall io. MonadIO io => Allocator -> Allocation -> Buffer -> io ()
+ VulkanMemoryAllocator: bindBufferMemory :: MonadIO io => Allocator -> Allocation -> Buffer -> io ()
- VulkanMemoryAllocator: bindBufferMemory2 :: forall io. MonadIO io => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> Buffer -> ("next" ::: Ptr ()) -> io ()
+ VulkanMemoryAllocator: bindBufferMemory2 :: MonadIO io => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> Buffer -> ("next" ::: Ptr ()) -> io ()
- VulkanMemoryAllocator: bindImageMemory :: forall io. MonadIO io => Allocator -> Allocation -> Image -> io ()
+ VulkanMemoryAllocator: bindImageMemory :: MonadIO io => Allocator -> Allocation -> Image -> io ()
- VulkanMemoryAllocator: bindImageMemory2 :: forall io. MonadIO io => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> Image -> ("next" ::: Ptr ()) -> io ()
+ VulkanMemoryAllocator: bindImageMemory2 :: MonadIO io => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> Image -> ("next" ::: Ptr ()) -> io ()
- VulkanMemoryAllocator: buildStatsString :: forall io. MonadIO io => Allocator -> ("detailedMap" ::: Bool) -> io ("statsString" ::: Ptr CChar)
+ VulkanMemoryAllocator: buildStatsString :: MonadIO io => Allocator -> ("detailedMap" ::: Bool) -> io ("statsString" ::: Ptr CChar)
- VulkanMemoryAllocator: buildVirtualBlockStatsString :: forall io. MonadIO io => VirtualBlock -> ("detailedMap" ::: Bool) -> io ("statsString" ::: Ptr CChar)
+ VulkanMemoryAllocator: buildVirtualBlockStatsString :: MonadIO io => VirtualBlock -> ("detailedMap" ::: Bool) -> io ("statsString" ::: Ptr CChar)
- VulkanMemoryAllocator: calculatePoolStatistics :: forall io. MonadIO io => Allocator -> Pool -> io ("poolStats" ::: DetailedStatistics)
+ VulkanMemoryAllocator: calculatePoolStatistics :: MonadIO io => Allocator -> Pool -> io ("poolStats" ::: DetailedStatistics)
- VulkanMemoryAllocator: calculateStatistics :: forall io. MonadIO io => Allocator -> io ("stats" ::: TotalStatistics)
+ VulkanMemoryAllocator: calculateStatistics :: MonadIO io => Allocator -> io ("stats" ::: TotalStatistics)
- VulkanMemoryAllocator: calculateVirtualBlockStatistics :: forall io. MonadIO io => VirtualBlock -> io ("stats" ::: DetailedStatistics)
+ VulkanMemoryAllocator: calculateVirtualBlockStatistics :: MonadIO io => VirtualBlock -> io ("stats" ::: DetailedStatistics)
- VulkanMemoryAllocator: checkCorruption :: forall io. MonadIO io => Allocator -> ("memoryTypeBits" ::: Word32) -> io ()
+ VulkanMemoryAllocator: checkCorruption :: MonadIO io => Allocator -> ("memoryTypeBits" ::: Word32) -> io ()
- VulkanMemoryAllocator: checkPoolCorruption :: forall io. MonadIO io => Allocator -> Pool -> io ()
+ VulkanMemoryAllocator: checkPoolCorruption :: MonadIO io => Allocator -> Pool -> io ()
- VulkanMemoryAllocator: clearVirtualBlock :: forall io. MonadIO io => VirtualBlock -> io ()
+ VulkanMemoryAllocator: clearVirtualBlock :: MonadIO io => VirtualBlock -> io ()
- VulkanMemoryAllocator: createAliasingBuffer :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> BufferCreateInfo a -> io Buffer
+ VulkanMemoryAllocator: createAliasingBuffer :: forall (a :: [Type]) io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> BufferCreateInfo a -> io Buffer
- VulkanMemoryAllocator: createAliasingBuffer2 :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> BufferCreateInfo a -> io Buffer
+ VulkanMemoryAllocator: createAliasingBuffer2 :: forall (a :: [Type]) io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> BufferCreateInfo a -> io Buffer
- VulkanMemoryAllocator: createAliasingImage :: forall a io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ImageCreateInfo a -> io Image
+ VulkanMemoryAllocator: createAliasingImage :: forall (a :: [Type]) io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ImageCreateInfo a -> io Image
- VulkanMemoryAllocator: createAliasingImage2 :: forall a io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> ImageCreateInfo a -> io Image
+ VulkanMemoryAllocator: createAliasingImage2 :: forall (a :: [Type]) io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> Allocation -> ("allocationLocalOffset" ::: DeviceSize) -> ImageCreateInfo a -> io Image
- VulkanMemoryAllocator: createAllocator :: forall io. MonadIO io => AllocatorCreateInfo -> io Allocator
+ VulkanMemoryAllocator: createAllocator :: MonadIO io => AllocatorCreateInfo -> io Allocator
- VulkanMemoryAllocator: createBuffer :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> io (Buffer, Allocation, AllocationInfo)
+ VulkanMemoryAllocator: createBuffer :: forall (a :: [Type]) io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> io (Buffer, Allocation, AllocationInfo)
- VulkanMemoryAllocator: createBufferWithAlignment :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> ("minAlignment" ::: DeviceSize) -> io (Buffer, Allocation, AllocationInfo)
+ VulkanMemoryAllocator: createBufferWithAlignment :: forall (a :: [Type]) io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> ("minAlignment" ::: DeviceSize) -> io (Buffer, Allocation, AllocationInfo)
- VulkanMemoryAllocator: createImage :: forall a io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> io (Image, Allocation, AllocationInfo)
+ VulkanMemoryAllocator: createImage :: forall (a :: [Type]) io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> io (Image, Allocation, AllocationInfo)
- VulkanMemoryAllocator: createPool :: forall io. MonadIO io => Allocator -> PoolCreateInfo -> io Pool
+ VulkanMemoryAllocator: createPool :: MonadIO io => Allocator -> PoolCreateInfo -> io Pool
- VulkanMemoryAllocator: createVirtualBlock :: forall io. MonadIO io => VirtualBlockCreateInfo -> io VirtualBlock
+ VulkanMemoryAllocator: createVirtualBlock :: MonadIO io => VirtualBlockCreateInfo -> io VirtualBlock
- VulkanMemoryAllocator: destroyAllocator :: forall io. MonadIO io => Allocator -> io ()
+ VulkanMemoryAllocator: destroyAllocator :: MonadIO io => Allocator -> io ()
- VulkanMemoryAllocator: destroyBuffer :: forall io. MonadIO io => Allocator -> Buffer -> Allocation -> io ()
+ VulkanMemoryAllocator: destroyBuffer :: MonadIO io => Allocator -> Buffer -> Allocation -> io ()
- VulkanMemoryAllocator: destroyImage :: forall io. MonadIO io => Allocator -> Image -> Allocation -> io ()
+ VulkanMemoryAllocator: destroyImage :: MonadIO io => Allocator -> Image -> Allocation -> io ()
- VulkanMemoryAllocator: destroyPool :: forall io. MonadIO io => Allocator -> Pool -> io ()
+ VulkanMemoryAllocator: destroyPool :: MonadIO io => Allocator -> Pool -> io ()
- VulkanMemoryAllocator: destroyVirtualBlock :: forall io. MonadIO io => VirtualBlock -> io ()
+ VulkanMemoryAllocator: destroyVirtualBlock :: MonadIO io => VirtualBlock -> io ()
- VulkanMemoryAllocator: endDefragmentation :: forall io. MonadIO io => Allocator -> DefragmentationContext -> io DefragmentationStats
+ VulkanMemoryAllocator: endDefragmentation :: MonadIO io => Allocator -> DefragmentationContext -> io DefragmentationStats
- VulkanMemoryAllocator: endDefragmentationPass :: forall io. MonadIO io => Allocator -> DefragmentationContext -> io ("passInfo" ::: DefragmentationPassMoveInfo)
+ VulkanMemoryAllocator: endDefragmentationPass :: MonadIO io => Allocator -> DefragmentationContext -> io ("passInfo" ::: DefragmentationPassMoveInfo)
- VulkanMemoryAllocator: findMemoryTypeIndex :: forall io. MonadIO io => Allocator -> ("memoryTypeBits" ::: Word32) -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
+ VulkanMemoryAllocator: findMemoryTypeIndex :: MonadIO io => Allocator -> ("memoryTypeBits" ::: Word32) -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
- VulkanMemoryAllocator: findMemoryTypeIndexForBufferInfo :: forall a io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
+ VulkanMemoryAllocator: findMemoryTypeIndexForBufferInfo :: forall (a :: [Type]) io. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
- VulkanMemoryAllocator: findMemoryTypeIndexForImageInfo :: forall a io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
+ VulkanMemoryAllocator: findMemoryTypeIndexForImageInfo :: forall (a :: [Type]) io. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> io ("memoryTypeIndex" ::: Word32)
- VulkanMemoryAllocator: flushAllocation :: forall io. MonadIO io => Allocator -> Allocation -> ("offset" ::: DeviceSize) -> DeviceSize -> io ()
+ VulkanMemoryAllocator: flushAllocation :: MonadIO io => Allocator -> Allocation -> ("offset" ::: DeviceSize) -> DeviceSize -> io ()
- VulkanMemoryAllocator: flushAllocations :: forall io. MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> ("offsets" ::: Vector DeviceSize) -> ("sizes" ::: Vector DeviceSize) -> io ()
+ VulkanMemoryAllocator: flushAllocations :: MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> ("offsets" ::: Vector DeviceSize) -> ("sizes" ::: Vector DeviceSize) -> io ()
- VulkanMemoryAllocator: freeMemory :: forall io. MonadIO io => Allocator -> Allocation -> io ()
+ VulkanMemoryAllocator: freeMemory :: MonadIO io => Allocator -> Allocation -> io ()
- VulkanMemoryAllocator: freeMemoryPages :: forall io. MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> io ()
+ VulkanMemoryAllocator: freeMemoryPages :: MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> io ()
- VulkanMemoryAllocator: freeStatsString :: forall io. MonadIO io => Allocator -> ("statsString" ::: Ptr CChar) -> io ()
+ VulkanMemoryAllocator: freeStatsString :: MonadIO io => Allocator -> ("statsString" ::: Ptr CChar) -> io ()
- VulkanMemoryAllocator: freeVirtualBlockStatsString :: forall io. MonadIO io => VirtualBlock -> ("statsString" ::: Ptr CChar) -> io ()
+ VulkanMemoryAllocator: freeVirtualBlockStatsString :: MonadIO io => VirtualBlock -> ("statsString" ::: Ptr CChar) -> io ()
- VulkanMemoryAllocator: getAllocationInfo :: forall io. MonadIO io => Allocator -> Allocation -> io AllocationInfo
+ VulkanMemoryAllocator: getAllocationInfo :: MonadIO io => Allocator -> Allocation -> io AllocationInfo
- VulkanMemoryAllocator: getAllocationMemoryProperties :: forall io. MonadIO io => Allocator -> Allocation -> io MemoryPropertyFlags
+ VulkanMemoryAllocator: getAllocationMemoryProperties :: MonadIO io => Allocator -> Allocation -> io MemoryPropertyFlags
- VulkanMemoryAllocator: getAllocatorInfo :: forall io. MonadIO io => Allocator -> io AllocatorInfo
+ VulkanMemoryAllocator: getAllocatorInfo :: MonadIO io => Allocator -> io AllocatorInfo
- VulkanMemoryAllocator: getHeapBudgets :: forall io. MonadIO io => Allocator -> ("budgets" ::: Ptr Budget) -> io ()
+ VulkanMemoryAllocator: getHeapBudgets :: MonadIO io => Allocator -> ("budgets" ::: Ptr Budget) -> io ()
- VulkanMemoryAllocator: getMemoryProperties :: forall io. MonadIO io => Allocator -> io (Ptr PhysicalDeviceMemoryProperties)
+ VulkanMemoryAllocator: getMemoryProperties :: MonadIO io => Allocator -> io (Ptr PhysicalDeviceMemoryProperties)
- VulkanMemoryAllocator: getMemoryTypeProperties :: forall io. MonadIO io => Allocator -> ("memoryTypeIndex" ::: Word32) -> io MemoryPropertyFlags
+ VulkanMemoryAllocator: getMemoryTypeProperties :: MonadIO io => Allocator -> ("memoryTypeIndex" ::: Word32) -> io MemoryPropertyFlags
- VulkanMemoryAllocator: getPhysicalDeviceProperties :: forall io. MonadIO io => Allocator -> io (Ptr PhysicalDeviceProperties)
+ VulkanMemoryAllocator: getPhysicalDeviceProperties :: MonadIO io => Allocator -> io (Ptr PhysicalDeviceProperties)
- VulkanMemoryAllocator: getPoolName :: forall io. MonadIO io => Allocator -> Pool -> io ("name" ::: Ptr CChar)
+ VulkanMemoryAllocator: getPoolName :: MonadIO io => Allocator -> Pool -> io ("name" ::: Ptr CChar)
- VulkanMemoryAllocator: getPoolStatistics :: forall io. MonadIO io => Allocator -> Pool -> io ("poolStats" ::: Statistics)
+ VulkanMemoryAllocator: getPoolStatistics :: MonadIO io => Allocator -> Pool -> io ("poolStats" ::: Statistics)
- VulkanMemoryAllocator: getVirtualAllocationInfo :: forall io. MonadIO io => VirtualBlock -> VirtualAllocation -> io ("virtualAllocInfo" ::: VirtualAllocationInfo)
+ VulkanMemoryAllocator: getVirtualAllocationInfo :: MonadIO io => VirtualBlock -> VirtualAllocation -> io ("virtualAllocInfo" ::: VirtualAllocationInfo)
- VulkanMemoryAllocator: getVirtualBlockStatistics :: forall io. MonadIO io => VirtualBlock -> io ("stats" ::: Statistics)
+ VulkanMemoryAllocator: getVirtualBlockStatistics :: MonadIO io => VirtualBlock -> io ("stats" ::: Statistics)
- VulkanMemoryAllocator: invalidateAllocation :: forall io. MonadIO io => Allocator -> Allocation -> ("offset" ::: DeviceSize) -> DeviceSize -> io ()
+ VulkanMemoryAllocator: invalidateAllocation :: MonadIO io => Allocator -> Allocation -> ("offset" ::: DeviceSize) -> DeviceSize -> io ()
- VulkanMemoryAllocator: invalidateAllocations :: forall io. MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> ("offsets" ::: Vector DeviceSize) -> ("sizes" ::: Vector DeviceSize) -> io ()
+ VulkanMemoryAllocator: invalidateAllocations :: MonadIO io => Allocator -> ("allocations" ::: Vector Allocation) -> ("offsets" ::: Vector DeviceSize) -> ("sizes" ::: Vector DeviceSize) -> io ()
- VulkanMemoryAllocator: isVirtualBlockEmpty :: forall io. MonadIO io => VirtualBlock -> io Bool
+ VulkanMemoryAllocator: isVirtualBlockEmpty :: MonadIO io => VirtualBlock -> io Bool
- VulkanMemoryAllocator: mapMemory :: forall io. MonadIO io => Allocator -> Allocation -> io ("data" ::: Ptr ())
+ VulkanMemoryAllocator: mapMemory :: MonadIO io => Allocator -> Allocation -> io ("data" ::: Ptr ())
- VulkanMemoryAllocator: setAllocationName :: forall io. MonadIO io => Allocator -> Allocation -> ("name" ::: Maybe ByteString) -> io ()
+ VulkanMemoryAllocator: setAllocationName :: MonadIO io => Allocator -> Allocation -> ("name" ::: Maybe ByteString) -> io ()
- VulkanMemoryAllocator: setAllocationUserData :: forall io. MonadIO io => Allocator -> Allocation -> ("userData" ::: Ptr ()) -> io ()
+ VulkanMemoryAllocator: setAllocationUserData :: MonadIO io => Allocator -> Allocation -> ("userData" ::: Ptr ()) -> io ()
- VulkanMemoryAllocator: setCurrentFrameIndex :: forall io. MonadIO io => Allocator -> ("frameIndex" ::: Word32) -> io ()
+ VulkanMemoryAllocator: setCurrentFrameIndex :: MonadIO io => Allocator -> ("frameIndex" ::: Word32) -> io ()
- VulkanMemoryAllocator: setPoolName :: forall io. MonadIO io => Allocator -> Pool -> ("name" ::: Maybe ByteString) -> io ()
+ VulkanMemoryAllocator: setPoolName :: MonadIO io => Allocator -> Pool -> ("name" ::: Maybe ByteString) -> io ()
- VulkanMemoryAllocator: setVirtualAllocationUserData :: forall io. MonadIO io => VirtualBlock -> VirtualAllocation -> ("userData" ::: Ptr ()) -> io ()
+ VulkanMemoryAllocator: setVirtualAllocationUserData :: MonadIO io => VirtualBlock -> VirtualAllocation -> ("userData" ::: Ptr ()) -> io ()
- VulkanMemoryAllocator: type FN_vmaAllocateDeviceMemoryFunction = Allocator -> ("memoryType" ::: Word32) -> DeviceMemory -> DeviceSize -> ("pUserData" ::: Ptr ()) -> IO ()
+ VulkanMemoryAllocator: type FN_vmaAllocateDeviceMemoryFunction = Allocator -> "memoryType" ::: Word32 -> DeviceMemory -> DeviceSize -> "pUserData" ::: Ptr () -> IO ()
- VulkanMemoryAllocator: type FN_vmaCheckDefragmentationBreakFunction = ("pUserData" ::: Ptr ()) -> IO Bool32
+ VulkanMemoryAllocator: type FN_vmaCheckDefragmentationBreakFunction = "pUserData" ::: Ptr () -> IO Bool32
- VulkanMemoryAllocator: type FN_vmaFreeDeviceMemoryFunction = Allocator -> ("memoryType" ::: Word32) -> DeviceMemory -> DeviceSize -> ("pUserData" ::: Ptr ()) -> IO ()
+ VulkanMemoryAllocator: type FN_vmaFreeDeviceMemoryFunction = Allocator -> "memoryType" ::: Word32 -> DeviceMemory -> DeviceSize -> "pUserData" ::: Ptr () -> IO ()
- VulkanMemoryAllocator: unmapMemory :: forall io. MonadIO io => Allocator -> Allocation -> io ()
+ VulkanMemoryAllocator: unmapMemory :: MonadIO io => Allocator -> Allocation -> io ()
- VulkanMemoryAllocator: useDefragmentationPass :: forall io r. MonadIO io => Allocator -> DefragmentationContext -> (DefragmentationPassMoveInfo -> io r) -> io ("passInfo" ::: DefragmentationPassMoveInfo, r)
+ VulkanMemoryAllocator: useDefragmentationPass :: MonadIO io => Allocator -> DefragmentationContext -> (DefragmentationPassMoveInfo -> io r) -> io ("passInfo" ::: DefragmentationPassMoveInfo, r)
- VulkanMemoryAllocator: virtualAllocate :: forall io. MonadIO io => VirtualBlock -> VirtualAllocationCreateInfo -> io (VirtualAllocation, "offset" ::: DeviceSize)
+ VulkanMemoryAllocator: virtualAllocate :: MonadIO io => VirtualBlock -> VirtualAllocationCreateInfo -> io (VirtualAllocation, "offset" ::: DeviceSize)
- VulkanMemoryAllocator: virtualFree :: forall io. MonadIO io => VirtualBlock -> VirtualAllocation -> io ()
+ VulkanMemoryAllocator: virtualFree :: MonadIO io => VirtualBlock -> VirtualAllocation -> io ()
- VulkanMemoryAllocator: withAllocator :: forall io r. MonadIO io => AllocatorCreateInfo -> (io Allocator -> (Allocator -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withAllocator :: MonadIO io => AllocatorCreateInfo -> (io Allocator -> (Allocator -> io ()) -> r) -> r
- VulkanMemoryAllocator: withBuffer :: forall a io r. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> (io (Buffer, Allocation, AllocationInfo) -> ((Buffer, Allocation, AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withBuffer :: forall (a :: [Type]) io r. (Extendss BufferCreateInfo a, PokeChain a, MonadIO io) => Allocator -> BufferCreateInfo a -> AllocationCreateInfo -> (io (Buffer, Allocation, AllocationInfo) -> ((Buffer, Allocation, AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withDefragmentation :: forall io r. MonadIO io => Allocator -> DefragmentationInfo -> (io DefragmentationContext -> (DefragmentationContext -> io DefragmentationStats) -> r) -> r
+ VulkanMemoryAllocator: withDefragmentation :: MonadIO io => Allocator -> DefragmentationInfo -> (io DefragmentationContext -> (DefragmentationContext -> io DefragmentationStats) -> r) -> r
- VulkanMemoryAllocator: withImage :: forall a io r. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> (io (Image, Allocation, AllocationInfo) -> ((Image, Allocation, AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withImage :: forall (a :: [Type]) io r. (Extendss ImageCreateInfo a, PokeChain a, MonadIO io) => Allocator -> ImageCreateInfo a -> AllocationCreateInfo -> (io (Image, Allocation, AllocationInfo) -> ((Image, Allocation, AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withMappedMemory :: forall io r. MonadIO io => Allocator -> Allocation -> (io (Ptr ()) -> (Ptr () -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withMappedMemory :: MonadIO io => Allocator -> Allocation -> (io (Ptr ()) -> (Ptr () -> io ()) -> r) -> r
- VulkanMemoryAllocator: withMemory :: forall io r. MonadIO io => Allocator -> MemoryRequirements -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withMemory :: MonadIO io => Allocator -> MemoryRequirements -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withMemoryForBuffer :: forall io r. MonadIO io => Allocator -> Buffer -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withMemoryForBuffer :: MonadIO io => Allocator -> Buffer -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withMemoryForImage :: forall io r. MonadIO io => Allocator -> Image -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withMemoryForImage :: MonadIO io => Allocator -> Image -> AllocationCreateInfo -> (io (Allocation, AllocationInfo) -> ((Allocation, AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withMemoryPages :: forall io r. MonadIO io => Allocator -> Vector MemoryRequirements -> Vector AllocationCreateInfo -> (io (Vector Allocation, Vector AllocationInfo) -> ((Vector Allocation, Vector AllocationInfo) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withMemoryPages :: MonadIO io => Allocator -> Vector MemoryRequirements -> Vector AllocationCreateInfo -> (io (Vector Allocation, Vector AllocationInfo) -> ((Vector Allocation, Vector AllocationInfo) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withPool :: forall io r. MonadIO io => Allocator -> PoolCreateInfo -> (io Pool -> (Pool -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withPool :: MonadIO io => Allocator -> PoolCreateInfo -> (io Pool -> (Pool -> io ()) -> r) -> r
- VulkanMemoryAllocator: withVirtualAllocation :: forall io r. MonadIO io => VirtualBlock -> VirtualAllocationCreateInfo -> (io (VirtualAllocation, DeviceSize) -> ((VirtualAllocation, DeviceSize) -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withVirtualAllocation :: MonadIO io => VirtualBlock -> VirtualAllocationCreateInfo -> (io (VirtualAllocation, DeviceSize) -> ((VirtualAllocation, DeviceSize) -> io ()) -> r) -> r
- VulkanMemoryAllocator: withVirtualBlock :: forall io r. MonadIO io => VirtualBlockCreateInfo -> (io VirtualBlock -> (VirtualBlock -> io ()) -> r) -> r
+ VulkanMemoryAllocator: withVirtualBlock :: MonadIO io => VirtualBlockCreateInfo -> (io VirtualBlock -> (VirtualBlock -> io ()) -> r) -> r

Files

− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
VulkanMemoryAllocator.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.39.1. -- -- see: https://github.com/sol/hpack  name:           VulkanMemoryAllocator-version:        0.11.0.1+version:        0.11.1.0 synopsis:       Bindings to the VulkanMemoryAllocator library category:       Graphics homepage:       https://github.com/expipiplus1/vulkan#readme@@ -47,8 +47,10 @@ library   exposed-modules:       VulkanMemoryAllocator+      VulkanMemoryAllocator.Utils   hs-source-dirs:       src+      src-manual   default-extensions:       AllowAmbiguousTypes       CPP@@ -94,7 +96,7 @@     , bytestring     , transformers     , vector-    , vulkan >=3.6 && <3.27+    , vulkan >=3.6 && <3.28   default-language: Haskell2010   if flag(safe-foreign-calls)     cpp-options: -DSAFE_FOREIGN_CALLS
VulkanMemoryAllocator/include/vk_mem_alloc.h view
@@ -1,19552 +1,19383 @@ //-// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved.-//-// 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.-//--#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H-#define AMD_VULKAN_MEMORY_ALLOCATOR_H--/** \mainpage Vulkan Memory Allocator--<b>Version 3.1.0-development</b>--Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. \n-License: MIT--<b>API documentation divided into groups:</b> [Modules](modules.html)--\section main_table_of_contents Table of contents--- <b>User guide</b>-  - \subpage quick_start-    - [Project setup](@ref quick_start_project_setup)-    - [Initialization](@ref quick_start_initialization)-    - [Resource allocation](@ref quick_start_resource_allocation)-  - \subpage choosing_memory_type-    - [Usage](@ref choosing_memory_type_usage)-    - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)-    - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)-    - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)-    - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)-  - \subpage memory_mapping-    - [Mapping functions](@ref memory_mapping_mapping_functions)-    - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)-    - [Cache flush and invalidate](@ref memory_mapping_cache_control)-  - \subpage staying_within_budget-    - [Querying for budget](@ref staying_within_budget_querying_for_budget)-    - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)-  - \subpage resource_aliasing-  - \subpage custom_memory_pools-    - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)-    - [Linear allocation algorithm](@ref linear_algorithm)-      - [Free-at-once](@ref linear_algorithm_free_at_once)-      - [Stack](@ref linear_algorithm_stack)-      - [Double stack](@ref linear_algorithm_double_stack)-      - [Ring buffer](@ref linear_algorithm_ring_buffer)-  - \subpage defragmentation-  - \subpage statistics-    - [Numeric statistics](@ref statistics_numeric_statistics)-    - [JSON dump](@ref statistics_json_dump)-  - \subpage allocation_annotation-    - [Allocation user data](@ref allocation_user_data)-    - [Allocation names](@ref allocation_names)-  - \subpage virtual_allocator-  - \subpage debugging_memory_usage-    - [Memory initialization](@ref debugging_memory_usage_initialization)-    - [Margins](@ref debugging_memory_usage_margins)-    - [Corruption detection](@ref debugging_memory_usage_corruption_detection)-  - \subpage opengl_interop-- \subpage usage_patterns-    - [GPU-only resource](@ref usage_patterns_gpu_only)-    - [Staging copy for upload](@ref usage_patterns_staging_copy_upload)-    - [Readback](@ref usage_patterns_readback)-    - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading)-    - [Other use cases](@ref usage_patterns_other_use_cases)-- \subpage configuration-  - [Pointers to Vulkan functions](@ref config_Vulkan_functions)-  - [Custom host memory allocator](@ref custom_memory_allocator)-  - [Device memory allocation callbacks](@ref allocation_callbacks)-  - [Device heap memory limit](@ref heap_memory_limit)-- <b>Extension support</b>-    - \subpage vk_khr_dedicated_allocation-    - \subpage enabling_buffer_device_address-    - \subpage vk_ext_memory_priority-    - \subpage vk_amd_device_coherent_memory-- \subpage general_considerations-  - [Thread safety](@ref general_considerations_thread_safety)-  - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)-  - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)-  - [Allocation algorithm](@ref general_considerations_allocation_algorithm)-  - [Features not supported](@ref general_considerations_features_not_supported)--\section main_see_also See also--- [**Product page on GPUOpen**](https://gpuopen.com/gaming-product/vulkan-memory-allocator/)-- [**Source repository on GitHub**](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)--\defgroup group_init Library initialization--\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object.--\defgroup group_alloc Memory allocation--\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images.-Most basic ones being: vmaCreateBuffer(), vmaCreateImage().--\defgroup group_virtual Virtual allocator--\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm-for user-defined purpose without allocating any real GPU memory.--\defgroup group_stats Statistics--\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format.-See documentation chapter: \ref statistics.-*/---#ifdef __cplusplus-extern "C" {-#endif--#include <vulkan/vulkan.h>--#if !defined(VMA_VULKAN_VERSION)-    #if defined(VK_VERSION_1_3)-        #define VMA_VULKAN_VERSION 1003000-    #elif defined(VK_VERSION_1_2)-        #define VMA_VULKAN_VERSION 1002000-    #elif defined(VK_VERSION_1_1)-        #define VMA_VULKAN_VERSION 1001000-    #else-        #define VMA_VULKAN_VERSION 1000000-    #endif-#endif--#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS-    extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;-    extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;-    extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;-    extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;-    extern PFN_vkAllocateMemory vkAllocateMemory;-    extern PFN_vkFreeMemory vkFreeMemory;-    extern PFN_vkMapMemory vkMapMemory;-    extern PFN_vkUnmapMemory vkUnmapMemory;-    extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;-    extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;-    extern PFN_vkBindBufferMemory vkBindBufferMemory;-    extern PFN_vkBindImageMemory vkBindImageMemory;-    extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;-    extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;-    extern PFN_vkCreateBuffer vkCreateBuffer;-    extern PFN_vkDestroyBuffer vkDestroyBuffer;-    extern PFN_vkCreateImage vkCreateImage;-    extern PFN_vkDestroyImage vkDestroyImage;-    extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;-    #if VMA_VULKAN_VERSION >= 1001000-        extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;-        extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;-        extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;-        extern PFN_vkBindImageMemory2 vkBindImageMemory2;-        extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;-    #endif // #if VMA_VULKAN_VERSION >= 1001000-#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES--#if !defined(VMA_DEDICATED_ALLOCATION)-    #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation-        #define VMA_DEDICATED_ALLOCATION 1-    #else-        #define VMA_DEDICATED_ALLOCATION 0-    #endif-#endif--#if !defined(VMA_BIND_MEMORY2)-    #if VK_KHR_bind_memory2-        #define VMA_BIND_MEMORY2 1-    #else-        #define VMA_BIND_MEMORY2 0-    #endif-#endif--#if !defined(VMA_MEMORY_BUDGET)-    #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)-        #define VMA_MEMORY_BUDGET 1-    #else-        #define VMA_MEMORY_BUDGET 0-    #endif-#endif--// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.-#if !defined(VMA_BUFFER_DEVICE_ADDRESS)-    #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000-        #define VMA_BUFFER_DEVICE_ADDRESS 1-    #else-        #define VMA_BUFFER_DEVICE_ADDRESS 0-    #endif-#endif--// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.-#if !defined(VMA_MEMORY_PRIORITY)-    #if VK_EXT_memory_priority-        #define VMA_MEMORY_PRIORITY 1-    #else-        #define VMA_MEMORY_PRIORITY 0-    #endif-#endif--// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.-#if !defined(VMA_EXTERNAL_MEMORY)-    #if VK_KHR_external_memory-        #define VMA_EXTERNAL_MEMORY 1-    #else-        #define VMA_EXTERNAL_MEMORY 0-    #endif-#endif--// Define these macros to decorate all public functions with additional code,-// before and after returned type, appropriately. This may be useful for-// exporting the functions when compiling VMA as a separate library. Example:-// #define VMA_CALL_PRE  __declspec(dllexport)-// #define VMA_CALL_POST __cdecl-#ifndef VMA_CALL_PRE-    #define VMA_CALL_PRE-#endif-#ifndef VMA_CALL_POST-    #define VMA_CALL_POST-#endif--// Define this macro to decorate pNext pointers with an attribute specifying the Vulkan-// structure that will be extended via the pNext chain.-#ifndef VMA_EXTENDS_VK_STRUCT-    #define VMA_EXTENDS_VK_STRUCT(vkStruct)-#endif--// Define this macro to decorate pointers with an attribute specifying the-// length of the array they point to if they are not null.-//-// The length may be one of-// - The name of another parameter in the argument list where the pointer is declared-// - The name of another member in the struct where the pointer is declared-// - The name of a member of a struct type, meaning the value of that member in-//   the context of the call. For example-//   VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),-//   this means the number of memory heaps available in the device associated-//   with the VmaAllocator being dealt with.-#ifndef VMA_LEN_IF_NOT_NULL-    #define VMA_LEN_IF_NOT_NULL(len)-#endif--// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.-// see: https://clang.llvm.org/docs/AttributeReference.html#nullable-#ifndef VMA_NULLABLE-    #ifdef __clang__-        #define VMA_NULLABLE _Nullable-    #else-        #define VMA_NULLABLE-    #endif-#endif--// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.-// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull-#ifndef VMA_NOT_NULL-    #ifdef __clang__-        #define VMA_NOT_NULL _Nonnull-    #else-        #define VMA_NOT_NULL-    #endif-#endif--// If non-dispatchable handles are represented as pointers then we can give-// then nullability annotations-#ifndef VMA_NOT_NULL_NON_DISPATCHABLE-    #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)-        #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL-    #else-        #define VMA_NOT_NULL_NON_DISPATCHABLE-    #endif-#endif--#ifndef VMA_NULLABLE_NON_DISPATCHABLE-    #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)-        #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE-    #else-        #define VMA_NULLABLE_NON_DISPATCHABLE-    #endif-#endif--#ifndef VMA_STATS_STRING_ENABLED-    #define VMA_STATS_STRING_ENABLED 1-#endif--////////////////////////////////////////////////////////////////////////////////-////////////////////////////////////////////////////////////////////////////////-//-//    INTERFACE-//-////////////////////////////////////////////////////////////////////////////////-////////////////////////////////////////////////////////////////////////////////--// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE.-#ifndef _VMA_ENUM_DECLARATIONS--/**-\addtogroup group_init-@{-*/--/// Flags for created #VmaAllocator.-typedef enum VmaAllocatorCreateFlagBits-{-    /** \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.-    */-    VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,-    /** \brief Enables usage of VK_KHR_dedicated_allocation extension.--    The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.-    When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.--    Using this extension 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-    VmaAllocatorCreateInfo::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.-    */-    VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,-    /**-    Enables usage of VK_KHR_bind_memory2 extension.--    The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.-    When it is `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 VmaAllocatorCreateInfo::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 vmaBindBufferMemory2() or vmaBindImageMemory2().-    */-    VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,-    /**-    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 VmaAllocatorCreateInfo::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.-    */-    VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,-    /**-    Enables usage of VK_AMD_device_coherent_memory extension.--    You may set this flag only if you:--    - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,-    - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,-    - want it to be used internally by this library.--    The extension and accompanying device feature provide access to memory types with-    `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.-    They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.--    When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.-    To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,-    returning `VK_ERROR_FEATURE_NOT_PRESENT`.-    */-    VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,-    /**-    Enables usage of "buffer device address" feature, which allows you to use function-    `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.--    You may set this flag only if you:--    1. (For Vulkan version < 1.2) Found as available and enabled device extension-    VK_KHR_buffer_device_address.-    This extension is promoted to core Vulkan 1.2.-    2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.--    When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.-    The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to-    allocated memory blocks wherever it might be needed.--    For more information, see documentation chapter \ref enabling_buffer_device_address.-    */-    VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,-    /**-    Enables usage of VK_EXT_memory_priority extension in the library.--    You may set this flag only if you found available and enabled this device extension,-    along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,-    while creating Vulkan device passed as VmaAllocatorCreateInfo::device.--    When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority-    are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.--    A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.-    Larger values are higher priority. The granularity of the priorities is implementation-dependent.-    It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.-    The value to be used for default priority is 0.5.-    For more details, see the documentation of the VK_EXT_memory_priority extension.-    */-    VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,--    VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaAllocatorCreateFlagBits;-/// See #VmaAllocatorCreateFlagBits.-typedef VkFlags VmaAllocatorCreateFlags;--/** @} */--/**-\addtogroup group_alloc-@{-*/--/// \brief Intended usage of the allocated memory.-typedef enum VmaMemoryUsage-{-    /** No intended memory usage specified.-    Use other members of VmaAllocationCreateInfo to specify your requirements.-    */-    VMA_MEMORY_USAGE_UNKNOWN = 0,-    /**-    \deprecated Obsolete, preserved for backward compatibility.-    Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.-    */-    VMA_MEMORY_USAGE_GPU_ONLY = 1,-    /**-    \deprecated Obsolete, preserved for backward compatibility.-    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`.-    */-    VMA_MEMORY_USAGE_CPU_ONLY = 2,-    /**-    \deprecated Obsolete, preserved for backward compatibility.-    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.-    */-    VMA_MEMORY_USAGE_CPU_TO_GPU = 3,-    /**-    \deprecated Obsolete, preserved for backward compatibility.-    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.-    */-    VMA_MEMORY_USAGE_GPU_TO_CPU = 4,-    /**-    \deprecated Obsolete, preserved for backward compatibility.-    Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.-    */-    VMA_MEMORY_USAGE_CPU_COPY = 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.-    */-    VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,-    /**-    Selects best memory type automatically.-    This flag is recommended for most common use cases.--    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),-    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT-    in VmaAllocationCreateInfo::flags.--    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.-    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()-    and not with generic memory allocation functions.-    */-    VMA_MEMORY_USAGE_AUTO = 7,-    /**-    Selects best memory type automatically with preference for GPU (device) memory.--    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),-    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT-    in VmaAllocationCreateInfo::flags.--    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.-    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()-    and not with generic memory allocation functions.-    */-    VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8,-    /**-    Selects best memory type automatically with preference for CPU (host) memory.--    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),-    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT-    in VmaAllocationCreateInfo::flags.--    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.-    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()-    and not with generic memory allocation functions.-    */-    VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9,--    VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF-} VmaMemoryUsage;--/// Flags to be passed as VmaAllocationCreateInfo::flags.-typedef enum VmaAllocationCreateFlagBits-{-    /** \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.-    */-    VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,--    /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.--    If new allocation cannot be placed in any of the existing blocks, allocation-    fails with `VK_ERROR_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.-    */-    VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,-    /** \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 VmaAllocationInfo::pMappedData.--    It is 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).-    */-    VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,-    /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead.--    Set this flag to treat VmaAllocationCreateInfo::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 `pName`. The string is automatically-    freed together with the allocation. It is also used in vmaBuildStatsString().-    */-    VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,-    /** 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.-    */-    VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,-    /** 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: vmaCreateBuffer(), vmaCreateImage().-    Otherwise it is ignored.--    If you want to make sure the new buffer/image is not tied to the new memory allocation-    through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block,-    use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT.-    */-    VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,-    /** Create allocation only if additional device memory required for it, if any, won't exceed-    memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.-    */-    VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,-    /** \brief Set this flag if the allocated memory will have aliasing resources.--    Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified.-    Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors.-    */-    VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200,-    /**-    Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).--    - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,-      you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.-    - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.-      This includes allocations created in \ref custom_memory_pools.--    Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number,-    never read or accessed randomly, so a memory type can be selected that is uncached and write-combined.--    \warning Violating this declaration may work correctly, but will likely be very slow.-    Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;`-    Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once.-    */-    VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400,-    /**-    Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).--    - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,-      you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.-    - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.-      This includes allocations created in \ref custom_memory_pools.--    Declares that mapped memory can be read, written, and accessed in random order,-    so a `HOST_CACHED` memory type is required.-    */-    VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800,-    /**-    Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,-    it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected-    if it may improve performance.--    By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type-    (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and-    issue an explicit transfer to write/read your data.-    To prepare for this possibility, don't forget to add appropriate flags like-    `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image.-    */-    VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000,-    /** Allocation strategy that chooses smallest possible free range for the allocation-    to minimize memory usage and fragmentation, possibly at the expense of allocation time.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000,-    /** Allocation strategy that chooses first suitable free range for the allocation --    not necessarily in terms of the smallest offset but the one that is easiest and fastest to find-    to minimize allocation time, possibly at the expense of allocation quality.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000,-    /** Allocation strategy that chooses always the lowest offset in available space.-    This is not the most efficient strategy but achieves highly packed data.-    Used internally by defragmentation, not recommended in typical usage.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT  = 0x00040000,-    /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,-    /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,-    /** A bit mask to extract only `STRATEGY` bits from entire set of flags.-    */-    VMA_ALLOCATION_CREATE_STRATEGY_MASK =-        VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT |-        VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT |-        VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,--    VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaAllocationCreateFlagBits;-/// See #VmaAllocationCreateFlagBits.-typedef VkFlags VmaAllocationCreateFlags;--/// Flags to be passed as VmaPoolCreateInfo::flags.-typedef enum VmaPoolCreateFlagBits-{-    /** \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 vmaCreateBuffer(), vmaCreateImage(),-    vmaAllocateMemoryForBuffer(), 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 vmaAllocateMemoryForImage() or vmaAllocateMemory(),-    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.-    */-    VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,--    /** \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.-    */-    VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,--    /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.-    */-    VMA_POOL_CREATE_ALGORITHM_MASK =-        VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT,--    VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaPoolCreateFlagBits;-/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.-typedef VkFlags VmaPoolCreateFlags;--/// Flags to be passed as VmaDefragmentationInfo::flags.-typedef enum VmaDefragmentationFlagBits-{-    /* \brief Use simple but fast algorithm for defragmentation.-    May not achieve best results but will require least time to compute and least allocations to copy.-    */-    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1,-    /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified.-    Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved.-    */-    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2,-    /* \brief Perform full defragmentation of memory.-    Can result in notably more time to compute and allocations to copy, but will achieve best memory packing.-    */-    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4,-    /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make.-    Only available when bufferImageGranularity is greater than 1, since it aims to reduce-    alignment issues between different types of resources.-    Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT.-    */-    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8,--    /// A bit mask to extract only `ALGORITHM` bits from entire set of flags.-    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK =-        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT |-        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT |-        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT |-        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT,--    VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaDefragmentationFlagBits;-/// See #VmaDefragmentationFlagBits.-typedef VkFlags VmaDefragmentationFlags;--/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove.-typedef enum VmaDefragmentationMoveOperation-{-    /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass().-    VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0,-    /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged.-    VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1,-    /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed.-    VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2,-} VmaDefragmentationMoveOperation;--/** @} */--/**-\addtogroup group_virtual-@{-*/--/// Flags to be passed as VmaVirtualBlockCreateInfo::flags.-typedef enum VmaVirtualBlockCreateFlagBits-{-    /** \brief Enables alternative, linear allocation algorithm in this virtual block.--    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.-    */-    VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,--    /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.-    */-    VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =-        VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT,--    VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaVirtualBlockCreateFlagBits;-/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.-typedef VkFlags VmaVirtualBlockCreateFlags;--/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.-typedef enum VmaVirtualAllocationCreateFlagBits-{-    /** \brief Allocation will be created from upper stack in a double stack pool.--    This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.-    */-    VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,-    /** \brief Allocation strategy that tries to minimize memory usage.-    */-    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,-    /** \brief Allocation strategy that tries to minimize allocation time.-    */-    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,-    /** Allocation strategy that chooses always the lowest offset in available space.-    This is not the most efficient strategy but achieves highly packed data.-    */-    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,-    /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.--    These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.-    */-    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,--    VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF-} VmaVirtualAllocationCreateFlagBits;-/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.-typedef VkFlags VmaVirtualAllocationCreateFlags;--/** @} */--#endif // _VMA_ENUM_DECLARATIONS--#ifndef _VMA_DATA_TYPES_DECLARATIONS--/**-\addtogroup group_init-@{ */--/** \struct VmaAllocator-\brief Represents main object of this library initialized.--Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.-Call function vmaDestroyAllocator() to destroy it.--It is recommended to create just one object of this type per `VkDevice` object,-right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.-*/-VK_DEFINE_HANDLE(VmaAllocator)--/** @} */--/**-\addtogroup group_alloc-@{-*/--/** \struct VmaPool-\brief Represents custom memory pool--Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.-Call function vmaDestroyPool() to destroy it.--For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).-*/-VK_DEFINE_HANDLE(VmaPool)--/** \struct VmaAllocation-\brief Represents single memory allocation.--It may be either dedicated block of `VkDeviceMemory` 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 VmaAllocationCreateInfo.-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 vmaGetAllocationInfo() and inspect-returned structure VmaAllocationInfo.-*/-VK_DEFINE_HANDLE(VmaAllocation)--/** \struct VmaDefragmentationContext-\brief An opaque object that represents started defragmentation process.--Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it.-Call function vmaEndDefragmentation() to destroy it.-*/-VK_DEFINE_HANDLE(VmaDefragmentationContext)--/** @} */--/**-\addtogroup group_virtual-@{-*/--/** \struct VmaVirtualAllocation-\brief Represents single memory allocation done inside VmaVirtualBlock.--Use it as a unique identifier to virtual allocation within the single block.--Use value `VK_NULL_HANDLE` to represent a null/invalid allocation.-*/-VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation)--/** @} */--/**-\addtogroup group_virtual-@{-*/--/** \struct VmaVirtualBlock-\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.--Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.-For more information, see documentation chapter \ref virtual_allocator.--This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally.-*/-VK_DEFINE_HANDLE(VmaVirtualBlock)--/** @} */--/**-\addtogroup group_init-@{-*/--/// Callback function called after successful vkAllocateMemory.-typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(-    VmaAllocator VMA_NOT_NULL                    allocator,-    uint32_t                                     memoryType,-    VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,-    VkDeviceSize                                 size,-    void* VMA_NULLABLE                           pUserData);--/// Callback function called before vkFreeMemory.-typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(-    VmaAllocator VMA_NOT_NULL                    allocator,-    uint32_t                                     memoryType,-    VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,-    VkDeviceSize                                 size,-    void* VMA_NULLABLE                           pUserData);--/** \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 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.-*/-typedef struct VmaDeviceMemoryCallbacks-{-    /// Optional, can be null.-    PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;-    /// Optional, can be null.-    PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;-    /// Optional, can be null.-    void* VMA_NULLABLE pUserData;-} VmaDeviceMemoryCallbacks;--/** \brief Pointers to some Vulkan functions - a subset used by the library.--Used in VmaAllocatorCreateInfo::pVulkanFunctions.-*/-typedef struct VmaVulkanFunctions-{-    /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.-    PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;-    /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.-    PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;-    PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;-    PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;-    PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;-    PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;-    PFN_vkMapMemory VMA_NULLABLE vkMapMemory;-    PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;-    PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;-    PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;-    PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;-    PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;-    PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;-    PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;-    PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;-    PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;-    PFN_vkCreateImage VMA_NULLABLE vkCreateImage;-    PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;-    PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;-#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.-    PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;-    /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.-    PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;-#endif-#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000-    /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.-    PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;-    /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.-    PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;-#endif-#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;-#endif-#if VMA_VULKAN_VERSION >= 1003000-    /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.-    PFN_vkGetDeviceBufferMemoryRequirements VMA_NULLABLE vkGetDeviceBufferMemoryRequirements;-    /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.-    PFN_vkGetDeviceImageMemoryRequirements VMA_NULLABLE vkGetDeviceImageMemoryRequirements;-#endif-} VmaVulkanFunctions;--/// Description of a Allocator to be created.-typedef struct VmaAllocatorCreateInfo-{-    /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.-    VmaAllocatorCreateFlags flags;-    /// Vulkan physical device.-    /** It must be valid throughout whole lifetime of created allocator. */-    VkPhysicalDevice VMA_NOT_NULL physicalDevice;-    /// Vulkan device.-    /** It must be valid throughout whole lifetime of created allocator. */-    VkDevice VMA_NOT_NULL device;-    /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.-    /** Set to 0 to use default, which is currently 256 MiB. */-    VkDeviceSize preferredLargeHeapBlockSize;-    /// Custom CPU memory allocation callbacks. Optional.-    /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */-    const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;-    /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.-    /** Optional, can be null. */-    const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;-    /** \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-    `VkPhysicalDeviceMemoryProperties::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 `VK_ERROR_OUT_OF_DEVICE_MEMORY`.-    - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the-      value of this limit will be reported instead when using vmaGetMemoryProperties().--    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 `VK_ERROR_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.-    */-    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;--    /** \brief Pointers to Vulkan functions. Can be null.--    For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).-    */-    const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;-    /** \brief Handle to Vulkan instance object.--    Starting from version 3.0.0 this member is no longer optional, it must be set!-    */-    VkInstance VMA_NOT_NULL 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 equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`.-    Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation.-    Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.-    */-    uint32_t vulkanApiVersion;-#if VMA_EXTERNAL_MEMORY-    /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.--    If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`-    elements, defining external memory handle types of particular Vulkan memory type,-    to be passed using `VkExportMemoryAllocateInfoKHR`.--    Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.-    This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.-    */-    const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;-#endif // #if VMA_EXTERNAL_MEMORY-} VmaAllocatorCreateInfo;--/// Information about existing #VmaAllocator object.-typedef struct VmaAllocatorInfo-{-    /** \brief Handle to Vulkan instance object.--    This is the same value as has been passed through VmaAllocatorCreateInfo::instance.-    */-    VkInstance VMA_NOT_NULL instance;-    /** \brief Handle to Vulkan physical device object.--    This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.-    */-    VkPhysicalDevice VMA_NOT_NULL physicalDevice;-    /** \brief Handle to Vulkan device object.--    This is the same value as has been passed through VmaAllocatorCreateInfo::device.-    */-    VkDevice VMA_NOT_NULL device;-} VmaAllocatorInfo;--/** @} */--/**-\addtogroup group_stats-@{-*/--/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total.--These are fast to calculate.-See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics().-*/-typedef struct VmaStatistics-{-    /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated.-    */-    uint32_t blockCount;-    /** \brief Number of #VmaAllocation objects allocated.--    Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`.-    */-    uint32_t allocationCount;-    /** \brief Number of bytes allocated in `VkDeviceMemory` blocks.--    \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object-    (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls-    "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image.-    */-    VkDeviceSize blockBytes;-    /** \brief Total number of bytes occupied by all #VmaAllocation objects.--    Always less or equal than `blockBytes`.-    Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan-    but unused by any #VmaAllocation.-    */-    VkDeviceSize allocationBytes;-} VmaStatistics;--/** \brief More detailed statistics than #VmaStatistics.--These are slower to calculate. Use for debugging purposes.-See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics().--Previous version of the statistics API provided averages, but they have been removed-because they can be easily calculated as:--\code-VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount;-VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes;-VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount;-\endcode-*/-typedef struct VmaDetailedStatistics-{-    /// Basic statistics.-    VmaStatistics statistics;-    /// Number of free ranges of memory between allocations.-    uint32_t unusedRangeCount;-    /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations.-    VkDeviceSize allocationSizeMin;-    /// Largest allocation size. 0 if there are 0 allocations.-    VkDeviceSize allocationSizeMax;-    /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges.-    VkDeviceSize unusedRangeSizeMin;-    /// Largest empty range size. 0 if there are 0 empty ranges.-    VkDeviceSize unusedRangeSizeMax;-} VmaDetailedStatistics;--/** \brief  General statistics from current state of the Allocator --total memory usage across all memory heaps and types.--These are slower to calculate. Use for debugging purposes.-See function vmaCalculateStatistics().-*/-typedef struct VmaTotalStatistics-{-    VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES];-    VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS];-    VmaDetailedStatistics total;-} VmaTotalStatistics;--/** \brief Statistics of current memory usage and available budget for a specific memory heap.--These are fast to calculate.-See function vmaGetHeapBudgets().-*/-typedef struct VmaBudget-{-    /** \brief Statistics fetched from the library.-    */-    VmaStatistics statistics;-    /** \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 `statistics.blockBytes` (usually higher) due to additional implicit objects-    also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or-    `VkDeviceMemory` blocks allocated outside of this library, if any.-    */-    VkDeviceSize usage;-    /** \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 `VkMemoryHeap::size[heapIndex]` due to factors-    external to the program, decided by the operating system.-    Difference `budget - usage` is the amount of additional memory that can probably-    be allocated without problems. Exceeding the budget may result in various problems.-    */-    VkDeviceSize budget;-} VmaBudget;--/** @} */--/**-\addtogroup group_alloc-@{-*/--/** \brief Parameters of new #VmaAllocation.--To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others.-*/-typedef struct VmaAllocationCreateInfo-{-    /// Use #VmaAllocationCreateFlagBits enum.-    VmaAllocationCreateFlags flags;-    /** \brief Intended usage of memory.--    You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n-    If `pool` is not null, this member is ignored.-    */-    VmaMemoryUsage usage;-    /** \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.*/-    VkMemoryPropertyFlags requiredFlags;-    /** \brief Flags that preferably should be set in a memory type chosen for an allocation.--    Set to 0 if no additional flags are preferred. \n-    If `pool` is not null, this member is ignored. */-    VkMemoryPropertyFlags preferredFlags;-    /** \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.-    */-    uint32_t memoryTypeBits;-    /** \brief Pool that this allocation should be created in.--    Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:-    `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.-    */-    VmaPool VMA_NULLABLE pool;-    /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().--    If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, 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.-    */-    void* VMA_NULLABLE pUserData;-    /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.--    It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object-    and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.-    Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.-    */-    float priority;-} VmaAllocationCreateInfo;--/// Describes parameter of created #VmaPool.-typedef struct VmaPoolCreateInfo-{-    /** \brief Vulkan memory type index to allocate this pool from.-    */-    uint32_t memoryTypeIndex;-    /** \brief Use combination of #VmaPoolCreateFlagBits.-    */-    VmaPoolCreateFlags flags;-    /** \brief Size of a single `VkDeviceMemory` 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.-    In this case, the pool will also support dedicated allocations.-    */-    VkDeviceSize blockSize;-    /** \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.-    */-    size_t minBlockCount;-    /** \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 VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated-    throughout whole lifetime of this pool.-    */-    size_t maxBlockCount;-    /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.--    It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.-    Otherwise, this variable is ignored.-    */-    float priority;-    /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.--    Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.-    It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,-    e.g. when doing interop with OpenGL.-    */-    VkDeviceSize minAllocationAlignment;-    /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.--    Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.-    It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.-    Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.--    Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,-    can be attached automatically by this library when using other, more convenient of its features.-    */-    void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkMemoryAllocateInfo) pMemoryAllocateNext;-} VmaPoolCreateInfo;--/** @} */--/**-\addtogroup group_alloc-@{-*/--/// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().-typedef struct VmaAllocationInfo-{-    /** \brief Memory type index that this allocation was allocated from.--    It never changes.-    */-    uint32_t memoryType;-    /** \brief Handle to Vulkan memory object.--    Same memory object can be shared by multiple allocations.--    It can change after the allocation is moved during \ref defragmentation.-    */-    VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;-    /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.--    You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function-    vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,-    not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation-    and apply this offset automatically.--    It can change after the allocation is moved during \ref defragmentation.-    */-    VkDeviceSize offset;-    /** \brief Size of this allocation, in bytes.--    It never changes.--    \note Allocation size returned in this variable may be greater than the size-    requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the-    allocation is accessible for operations on memory e.g. using a pointer after-    mapping with vmaMapMemory(), but operations on the resource e.g. using-    `vkCmdCopyBuffer` must be limited to the size of the resource.-    */-    VkDeviceSize size;-    /** \brief Pointer to the beginning of this allocation as mapped data.--    If the allocation hasn't been mapped using vmaMapMemory() and hasn't been-    created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.--    It can change after call to vmaMapMemory(), vmaUnmapMemory().-    It can also change after the allocation is moved during \ref defragmentation.-    */-    void* VMA_NULLABLE pMappedData;-    /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().--    It can change after call to vmaSetAllocationUserData() for this allocation.-    */-    void* VMA_NULLABLE pUserData;-    /** \brief Custom allocation name that was set with vmaSetAllocationName().--    It can change after call to vmaSetAllocationName() for this allocation.--    Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with-    additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED].-    */-    const char* VMA_NULLABLE pName;-} VmaAllocationInfo;--/** Callback function called during vmaBeginDefragmentation() to check custom criterion about ending current defragmentation pass.--Should return true if the defragmentation needs to stop current pass.-*/-typedef VkBool32 (VKAPI_PTR* PFN_vmaCheckDefragmentationBreakFunction)(void* VMA_NULLABLE pUserData);--/** \brief Parameters for defragmentation.--To be used with function vmaBeginDefragmentation().-*/-typedef struct VmaDefragmentationInfo-{-    /// \brief Use combination of #VmaDefragmentationFlagBits.-    VmaDefragmentationFlags flags;-    /** \brief Custom pool to be defragmented.--    If null then default pools will undergo defragmentation process.-    */-    VmaPool VMA_NULLABLE pool;-    /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places.--    `0` means no limit.-    */-    VkDeviceSize maxBytesPerPass;-    /** \brief Maximum number of allocations that can be moved during single pass to a different place.--    `0` means no limit.-    */-    uint32_t maxAllocationsPerPass;-    /** \brief Optional custom callback for stopping vmaBeginDefragmentation().--    Have to return true for breaking current defragmentation pass.-    */-    PFN_vmaCheckDefragmentationBreakFunction VMA_NULLABLE pfnBreakCallback;-    /// \brief Optional data to pass to custom callback for stopping pass of defragmentation.-    void* VMA_NULLABLE pBreakCallbackUserData;-} VmaDefragmentationInfo;--/// Single move of an allocation to be done for defragmentation.-typedef struct VmaDefragmentationMove-{-    /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it.-    VmaDefragmentationMoveOperation operation;-    /// Allocation that should be moved.-    VmaAllocation VMA_NOT_NULL srcAllocation;-    /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`.--    \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass,-    to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory().-    vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory.-    */-    VmaAllocation VMA_NOT_NULL dstTmpAllocation;-} VmaDefragmentationMove;--/** \brief Parameters for incremental defragmentation steps.--To be used with function vmaBeginDefragmentationPass().-*/-typedef struct VmaDefragmentationPassMoveInfo-{-    /// Number of elements in the `pMoves` array.-    uint32_t moveCount;-    /** \brief Array of moves to be performed by the user in the current defragmentation pass.--    Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass().--    For each element, you should:--    1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset.-    2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`.-    3. Make sure these commands finished executing on the GPU.-    4. Destroy the old buffer/image.--    Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass().-    After this call, the allocation will point to the new place in memory.--    Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.--    Alternatively, if you decide you want to completely remove the allocation:--    1. Destroy its buffer/image.-    2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.--    Then, after vmaEndDefragmentationPass() the allocation will be freed.-    */-    VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves;-} VmaDefragmentationPassMoveInfo;--/// Statistics returned for defragmentation process in function vmaEndDefragmentation().-typedef struct VmaDefragmentationStats-{-    /// Total number of bytes that have been copied while moving allocations to different places.-    VkDeviceSize bytesMoved;-    /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.-    VkDeviceSize bytesFreed;-    /// Number of allocations that have been moved to different places.-    uint32_t allocationsMoved;-    /// Number of empty `VkDeviceMemory` objects that have been released to the system.-    uint32_t deviceMemoryBlocksFreed;-} VmaDefragmentationStats;--/** @} */--/**-\addtogroup group_virtual-@{-*/--/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().-typedef struct VmaVirtualBlockCreateInfo-{-    /** \brief Total size of the virtual block.--    Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.-    For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.-    */-    VkDeviceSize size;--    /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.-    */-    VmaVirtualBlockCreateFlags flags;--    /** \brief Custom CPU memory allocation callbacks. Optional.--    Optional, can be null. When specified, they will be used for all CPU-side memory allocations.-    */-    const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;-} VmaVirtualBlockCreateInfo;--/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().-typedef struct VmaVirtualAllocationCreateInfo-{-    /** \brief Size of the allocation.--    Cannot be zero.-    */-    VkDeviceSize size;-    /** \brief Required alignment of the allocation. Optional.--    Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.-    */-    VkDeviceSize alignment;-    /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.-    */-    VmaVirtualAllocationCreateFlags flags;-    /** \brief Custom pointer to be associated with the allocation. Optional.--    It can be any value and can be used for user-defined purposes. It can be fetched or changed later.-    */-    void* VMA_NULLABLE pUserData;-} VmaVirtualAllocationCreateInfo;--/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().-typedef struct VmaVirtualAllocationInfo-{-    /** \brief Offset of the allocation.--    Offset at which the allocation was made.-    */-    VkDeviceSize offset;-    /** \brief Size of the allocation.--    Same value as passed in VmaVirtualAllocationCreateInfo::size.-    */-    VkDeviceSize size;-    /** \brief Custom pointer associated with the allocation.--    Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().-    */-    void* VMA_NULLABLE pUserData;-} VmaVirtualAllocationInfo;--/** @} */--#endif // _VMA_DATA_TYPES_DECLARATIONS--#ifndef _VMA_FUNCTION_HEADERS--/**-\addtogroup group_init-@{-*/--/// Creates #VmaAllocator object.-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(-    const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);--/// Destroys allocator object.-VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(-    VmaAllocator VMA_NULLABLE allocator);--/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.--It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to-`VkPhysicalDevice`, `VkDevice` etc. every time using this function.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);--/**-PhysicalDeviceProperties are fetched from physicalDevice by the allocator.-You can access it here, without fetching it again on your own.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);--/**-PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.-You can access it here, without fetching it again on your own.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);--/**-\brief Given Memory Type Index, returns Property Flags of this memory type.--This is just a convenience function. Same information can be obtained using-vmaGetMemoryProperties().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t memoryTypeIndex,-    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);--/** \brief Sets index of the current frame.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t frameIndex);--/** @} */--/**-\addtogroup group_stats-@{-*/--/** \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. Use it for debugging purposes.-For faster but more brief statistics suitable to be called every frame or every allocation,-use vmaGetHeapBudgets().--Note that when using allocator from multiple threads, returned information may immediately-become outdated.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaTotalStatistics* VMA_NOT_NULL pStats);--/** \brief Retrieves information about current memory usage and budget for all memory heaps.--\param allocator-\param[out] pBudgets 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 vmaCalculateStatistics().--Note that when using allocator from multiple threads, returned information may immediately-become outdated.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets);--/** @} */--/**-\addtogroup group_alloc-@{-*/--/**-\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.--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 VK_ERROR_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.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t memoryTypeBits,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);--/**-\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.--It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.-It internally creates a temporary, dummy buffer that never has memory bound.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);--/**-\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.--It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.-It internally creates a temporary, dummy image that never has memory bound.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);--/** \brief Allocates Vulkan device memory and creates #VmaPool object.--\param allocator Allocator object.-\param pCreateInfo Parameters of pool to create.-\param[out] pPool Handle to created pool.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(-    VmaAllocator VMA_NOT_NULL allocator,-    const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);--/** \brief Destroys #VmaPool object and frees Vulkan device memory.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NULLABLE pool);--/** @} */--/**-\addtogroup group_stats-@{-*/--/** \brief Retrieves statistics of existing #VmaPool object.--\param allocator Allocator object.-\param pool Pool object.-\param[out] pPoolStats Statistics of specified pool.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NOT_NULL pool,-    VmaStatistics* VMA_NOT_NULL pPoolStats);--/** \brief Retrieves detailed statistics of existing #VmaPool object.--\param allocator Allocator object.-\param pool Pool object.-\param[out] pPoolStats Statistics of specified pool.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NOT_NULL pool,-    VmaDetailedStatistics* VMA_NOT_NULL pPoolStats);--/** @} */--/**-\addtogroup group_alloc-@{-*/--/** \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:--- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.-- `VK_SUCCESS` - corruption detection has been performed and succeeded.-- `VK_ERROR_UNKNOWN` - 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.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NOT_NULL pool);--/** \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 vmaSetPoolName().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NOT_NULL pool,-    const char* VMA_NULLABLE* VMA_NOT_NULL ppName);--/** \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.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaPool VMA_NOT_NULL pool,-    const char* VMA_NULLABLE pName);--/** \brief General purpose memory allocation.--\param allocator-\param pVkMemoryRequirements-\param pCreateInfo-\param[out] pAllocation Handle to allocated memory.-\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().--You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().--It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),-vmaCreateBuffer(), vmaCreateImage() instead whenever possible.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/** \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 allocation.-\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 vmaFreeMemory() or vmaFreeMemoryPages().--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 vmaAllocateMemory() `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 `VK_NULL_HANDLE`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,-    const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,-    size_t allocationCount,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,-    VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);--/** \brief Allocates memory suitable for given `VkBuffer`.--\param allocator-\param buffer-\param pCreateInfo-\param[out] pAllocation Handle to allocated memory.-\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().--It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory().--This is a special-purpose function. In most cases you should use vmaCreateBuffer().--You must free the allocation using vmaFreeMemory() when no longer needed.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(-    VmaAllocator VMA_NOT_NULL allocator,-    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/** \brief Allocates memory suitable for given `VkImage`.--\param allocator-\param image-\param pCreateInfo-\param[out] pAllocation Handle to allocated memory.-\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().--It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory().--This is a special-purpose function. In most cases you should use vmaCreateImage().--You must free the allocation using vmaFreeMemory() when no longer needed.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(-    VmaAllocator VMA_NOT_NULL allocator,-    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().--Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    const VmaAllocation VMA_NULLABLE allocation);--/** \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. vmaAllocateMemory(),-vmaAllocateMemoryPages() and other functions.-It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.--Allocations in `pAllocations` array can come from any memory pools and types.-Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(-    VmaAllocator VMA_NOT_NULL allocator,-    size_t allocationCount,-    const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);--/** \brief Returns current information about specified allocation.--Current parameters of given allocation are returned in `pAllocationInfo`.--Although this function doesn't lock any mutex, so it should be quite efficient,-you should avoid calling it too often.-You can retrieve same VmaAllocationInfo structure while creating your resource, from function-vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change-(e.g. due to defragmentation).-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);--/** \brief Sets pUserData in given allocation to new value.--The value of pointer `pUserData` is 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.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    void* VMA_NULLABLE pUserData);--/** \brief Sets pName in given allocation to new value.--`pName` 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 `pName`. String-passed as pName 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-`pName` is freed from memory.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const char* VMA_NULLABLE pName);--/**-\brief Given an allocation, returns Property Flags of its memory type.--This is just a convenience function. Same information can be obtained using-vmaGetAllocationInfo() + vmaGetMemoryProperties().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);--/** \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.--\warning-If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is-correctly offsetted to the beginning of region assigned to this particular allocation.-Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block.-You should not add VmaAllocationInfo::offset to it!--Mapping is internally reference-counted and synchronized, so despite raw Vulkan-function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`-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 vmaUnmapMemory() 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 vmaUnmapMemory() same number of times as you called vmaMapMemory().--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 vmaUnmapMemory() same number of times as you called-vmaMapMemory(). You must not call vmaUnmapMemory() 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 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 vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    void* VMA_NULLABLE* VMA_NOT_NULL ppData);--/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().--For details, see description of vmaMapMemory().--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 vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation);--/** \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`!!!--This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is-called, otherwise `VK_SUCCESS`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize offset,-    VkDeviceSize size);--/** \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`!!!--This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if-it is called, otherwise `VK_SUCCESS`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize offset,-    VkDeviceSize size);--/** \brief Flushes memory of given set of allocations.--Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.-For more information, see documentation of vmaFlushAllocation().--\param allocator-\param allocationCount-\param allocations-\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.-\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.--This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is-called, otherwise `VK_SUCCESS`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t allocationCount,-    const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,-    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,-    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);--/** \brief Invalidates memory of given set of allocations.--Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.-For more information, see documentation of vmaInvalidateAllocation().--\param allocator-\param allocationCount-\param allocations-\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero.-\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.--This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is-called, otherwise `VK_SUCCESS`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t allocationCount,-    const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,-    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,-    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);--/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.--\param allocator-\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:--- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.-- `VK_SUCCESS` - corruption detection has been performed and succeeded.-- `VK_ERROR_UNKNOWN` - 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.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(-    VmaAllocator VMA_NOT_NULL allocator,-    uint32_t memoryTypeBits);--/** \brief Begins defragmentation process.--\param allocator Allocator object.-\param pInfo Structure filled with parameters of defragmentation.-\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation.-\returns-- `VK_SUCCESS` if defragmentation can begin.-- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.--For more information about defragmentation, see documentation chapter:-[Defragmentation](@ref defragmentation).-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(-    VmaAllocator VMA_NOT_NULL allocator,-    const VmaDefragmentationInfo* VMA_NOT_NULL pInfo,-    VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);--/** \brief Ends defragmentation process.--\param allocator Allocator object.-\param context Context object that has been created by vmaBeginDefragmentation().-\param[out] pStats Optional stats for the defragmentation. Can be null.--Use this function to finish defragmentation started by vmaBeginDefragmentation().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaDefragmentationContext VMA_NOT_NULL context,-    VmaDefragmentationStats* VMA_NULLABLE pStats);--/** \brief Starts single defragmentation pass.--\param allocator Allocator object.-\param context Context object that has been created by vmaBeginDefragmentation().-\param[out] pPassInfo Computed information for current pass.-\returns-- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation.-- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(),-  and then preferably try another pass with vmaBeginDefragmentationPass().-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaDefragmentationContext VMA_NOT_NULL context,-    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);--/** \brief Ends single defragmentation pass.--\param allocator Allocator object.-\param context Context object that has been created by vmaBeginDefragmentation().-\param pPassInfo Computed information for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you.--Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible.--Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`.-After this call:--- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY-  (which is the default) will be pointing to the new destination place.-- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY-  will be freed.--If no more moves are possible you can end whole defragmentation.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaDefragmentationContext VMA_NOT_NULL context,-    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);--/** \brief Binds buffer to allocation.--Binds specified buffer to region of memory represented by specified allocation.-Gets `VkDeviceMemory` 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 `VkDeviceMemory` 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 vmaCreateBuffer() instead of this one.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);--/** \brief Binds buffer to allocation with additional parameters.--\param allocator-\param allocation-\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.-\param buffer-\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.--This function is similar to vmaBindBufferMemory(), but it provides additional parameters.--If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag-or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,-    const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindBufferMemoryInfoKHR) pNext);--/** \brief Binds image to allocation.--Binds specified image to region of memory represented by specified allocation.-Gets `VkDeviceMemory` 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 `VkDeviceMemory` 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 vmaCreateImage() instead of this one.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);--/** \brief Binds image to allocation with additional parameters.--\param allocator-\param allocation-\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.-\param image-\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.--This function is similar to vmaBindImageMemory(), but it provides additional parameters.--If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag-or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,-    const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindImageMemoryInfoKHR) pNext);--/** \brief Creates a new `VkBuffer`, allocates and binds memory for it.--\param allocator-\param pBufferCreateInfo-\param pAllocationCreateInfo-\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 vmaGetAllocationInfo().--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 vmaDestroyBuffer() or-separately, using `vkDestroyBuffer()` and vmaFreeMemory().--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-(#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.--\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,-although recommended as a good practice, is out of scope of this library and could be implemented-by the user as a higher-level logic on top of VMA.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/** \brief Creates a buffer with additional minimum alignment.--Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,-minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.-for interop with OpenGL.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    VkDeviceSize minAlignment,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/** \brief Creates a new `VkBuffer`, binds already created memory for it.--\param allocator-\param allocation Allocation that provides memory to be used for binding new buffer to it.-\param pBufferCreateInfo-\param[out] pBuffer Buffer that was created.--This function automatically:---# Creates buffer.--# Binds the buffer with the supplied memory.--If any of these operations fail, buffer is not created,-returned value is negative error code and `*pBuffer` is null.--If the function succeeded, you must destroy the buffer when you-no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding-allocation you can use convenience function vmaDestroyBuffer().--\note There is a new version of this function augmented with parameter `allocationLocalOffset` - see vmaCreateAliasingBuffer2().-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);--/** \brief Creates a new `VkBuffer`, binds already created memory for it.--\param allocator-\param allocation Allocation that provides memory to be used for binding new buffer to it.-\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the allocation. Normally it should be 0.-\param pBufferCreateInfo -\param[out] pBuffer Buffer that was created.--This function automatically:---# Creates buffer.--# Binds the buffer with the supplied memory.--If any of these operations fail, buffer is not created,-returned value is negative error code and `*pBuffer` is null.--If the function succeeded, you must destroy the buffer when you-no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding-allocation you can use convenience function vmaDestroyBuffer().--\note This is a new version of the function augmented with parameter `allocationLocalOffset`.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);--/** \brief Destroys Vulkan buffer and frees allocated memory.--This is just a convenience function equivalent to:--\code-vkDestroyBuffer(device, buffer, allocationCallbacks);-vmaFreeMemory(allocator, allocation);-\endcode--It is safe to pass null as buffer and/or allocation.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(-    VmaAllocator VMA_NOT_NULL allocator,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,-    VmaAllocation VMA_NULLABLE allocation);--/// Function similar to vmaCreateBuffer().-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(-    VmaAllocator VMA_NOT_NULL allocator,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,-    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,-    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);--/// Function similar to vmaCreateAliasingBuffer() but for images.-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);--/// Function similar to vmaCreateAliasingBuffer2() but for images.-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);--/** \brief Destroys Vulkan image and frees allocated memory.--This is just a convenience function equivalent to:--\code-vkDestroyImage(device, image, allocationCallbacks);-vmaFreeMemory(allocator, allocation);-\endcode--It is safe to pass null as image and/or allocation.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(-    VmaAllocator VMA_NOT_NULL allocator,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE image,-    VmaAllocation VMA_NULLABLE allocation);--/** @} */--/**-\addtogroup group_virtual-@{-*/--/** \brief Creates new #VmaVirtualBlock object.--\param pCreateInfo Parameters for creation.-\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(-    const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock);--/** \brief Destroys #VmaVirtualBlock object.--Please note that you should consciously handle virtual allocations that could remain unfreed in the block.-You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()-if you are sure this is what you want. If you do neither, an assert is called.--If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,-don't forget to free them.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(-    VmaVirtualBlock VMA_NULLABLE virtualBlock);--/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.-*/-VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock);--/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);--/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.--If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned-(despite the function doesn't ever allocate actual GPU memory).-`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`.--\param virtualBlock Virtual block-\param pCreateInfo Parameters for the allocation-\param[out] pAllocation Returned handle of the new allocation-\param[out] pOffset Returned offset of the new allocation. Optional, can be null.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,-    VkDeviceSize* VMA_NULLABLE pOffset);--/** \brief Frees virtual allocation inside given #VmaVirtualBlock.--It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation);--/** \brief Frees all virtual allocations inside given #VmaVirtualBlock.--You must either call this function or free each virtual allocation individually with vmaVirtualFree()-before destroying a virtual block. Otherwise, an assert is called.--If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,-don't forget to free it as well.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock);--/** \brief Changes custom pointer associated with given virtual allocation.-*/-VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,-    void* VMA_NULLABLE pUserData);--/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.--This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaStatistics* VMA_NOT_NULL pStats);--/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock.--This function is slow to call. Use for debugging purposes.-For less detailed statistics, see vmaGetVirtualBlockStatistics().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaDetailedStatistics* VMA_NOT_NULL pStats);--/** @} */--#if VMA_STATS_STRING_ENABLED-/**-\addtogroup group_stats-@{-*/--/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.-\param virtualBlock Virtual block.-\param[out] ppStatsString Returned string.-\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.--Returned string must be freed using vmaFreeVirtualBlockStatsString().-*/-VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,-    VkBool32 detailedMap);--/// Frees a string returned by vmaBuildVirtualBlockStatsString().-VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(-    VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    char* VMA_NULLABLE pStatsString);--/** \brief Builds and returns statistics as a null-terminated string in JSON format.-\param allocator-\param[out] ppStatsString Must be freed using vmaFreeStatsString() function.-\param detailedMap-*/-VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(-    VmaAllocator VMA_NOT_NULL allocator,-    char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,-    VkBool32 detailedMap);--VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(-    VmaAllocator VMA_NOT_NULL allocator,-    char* VMA_NULLABLE pStatsString);--/** @} */--#endif // VMA_STATS_STRING_ENABLED--#endif // _VMA_FUNCTION_HEADERS--#ifdef __cplusplus-}-#endif--#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H--////////////////////////////////////////////////////////////////////////////////-////////////////////////////////////////////////////////////////////////////////-//-//    IMPLEMENTATION-//-////////////////////////////////////////////////////////////////////////////////-////////////////////////////////////////////////////////////////////////////////--// For Visual Studio IntelliSense.-#if defined(__cplusplus) && defined(__INTELLISENSE__)-#define VMA_IMPLEMENTATION-#endif--#ifdef VMA_IMPLEMENTATION-#undef VMA_IMPLEMENTATION--#include <cstdint>-#include <cstdlib>-#include <cstring>-#include <utility>-#include <type_traits>--#if !defined(VMA_CPP20)-    #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20-        #define VMA_CPP20 1-    #else-        #define VMA_CPP20 0-    #endif-#endif--#ifdef _MSC_VER-    #include <intrin.h> // For functions like __popcnt, _BitScanForward etc.-#endif-#if VMA_CPP20-    #include <bit> // For std::popcount-#endif--#if VMA_STATS_STRING_ENABLED-    #include <cstdio> // For snprintf-#endif--/*******************************************************************************-CONFIGURATION SECTION--Define some of these macros before each #include of this header or change them-here if you need other then default behavior depending on your environment.-*/-#ifndef _VMA_CONFIGURATION--/*-Define this macro to 1 to make the library fetch pointers to Vulkan functions-internally, like:--    vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;-*/-#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)-    #define VMA_STATIC_VULKAN_FUNCTIONS 1-#endif--/*-Define this macro to 1 to make the library fetch pointers to Vulkan functions-internally, like:--    vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory");--To use this feature in new versions of VMA you now have to pass-VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as-VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null.-*/-#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)-    #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1-#endif--#ifndef VMA_USE_STL_SHARED_MUTEX-    #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17-        #define VMA_USE_STL_SHARED_MUTEX 1-    // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus-    // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.-    #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L-        #define VMA_USE_STL_SHARED_MUTEX 1-    #else-        #define VMA_USE_STL_SHARED_MUTEX 0-    #endif-#endif--/*-Define this macro to include custom header files without having to edit this file directly, e.g.:--    // Inside of "my_vma_configuration_user_includes.h":--    #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT-    #include "my_custom_min.h" // for my_custom_min-    #include <algorithm>-    #include <mutex>--    // Inside a different file, which includes "vk_mem_alloc.h":--    #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h"-    #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr)-    #define VMA_MIN(v1, v2)  (my_custom_min(v1, v2))-    #include "vk_mem_alloc.h"-    ...--The following headers are used in this CONFIGURATION section only, so feel free to-remove them if not needed.-*/-#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)-    #include <cassert> // for assert-    #include <algorithm> // for min, max-    #include <mutex>-#else-    #include VMA_CONFIGURATION_USER_INCLUDES_H-#endif--#ifndef VMA_NULL-   // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.-   #define VMA_NULL   nullptr-#endif--#ifndef VMA_FALLTHROUGH-    #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17-        #define VMA_FALLTHROUGH [[fallthrough]]-    #else-        #define VMA_FALLTHROUGH-    #endif-#endif--// Normal assert to check for programmer's errors, especially in Debug configuration.-#ifndef VMA_ASSERT-   #ifdef NDEBUG-       #define VMA_ASSERT(expr)-   #else-       #define VMA_ASSERT(expr)         assert(expr)-   #endif-#endif--// Assert that will be called very often, like inside data structures e.g. operator[].-// Making it non-empty can make program slow.-#ifndef VMA_HEAVY_ASSERT-   #ifdef NDEBUG-       #define VMA_HEAVY_ASSERT(expr)-   #else-       #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)-   #endif-#endif--// If your compiler is not compatible with C++17 and definition of-// aligned_alloc() function is missing, uncommenting following line may help:--//#include <malloc.h>--#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)-#include <cstdlib>-static void* vma_aligned_alloc(size_t alignment, size_t size)-{-    // alignment must be >= sizeof(void*)-    if(alignment < sizeof(void*))-    {-        alignment = sizeof(void*);-    }--    return memalign(alignment, size);-}-#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))-#include <cstdlib>--#if defined(__APPLE__)-#include <AvailabilityMacros.h>-#endif--static void* vma_aligned_alloc(size_t alignment, size_t size)-{-    // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4)-    // Therefore, for now disable this specific exception until a proper solution is found.-    //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))-    //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0-    //    // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only-    //    // with the MacOSX11.0 SDK in Xcode 12 (which is what adds-    //    // MAC_OS_X_VERSION_10_16), even though the function is marked-    //    // available for 10.15. That is why the preprocessor checks for 10.16 but-    //    // the __builtin_available checks for 10.15.-    //    // People who use C++17 could call aligned_alloc with the 10.15 SDK already.-    //    if (__builtin_available(macOS 10.15, iOS 13, *))-    //        return aligned_alloc(alignment, size);-    //#endif-    //#endif--    // alignment must be >= sizeof(void*)-    if(alignment < sizeof(void*))-    {-        alignment = sizeof(void*);-    }--    void *pointer;-    if(posix_memalign(&pointer, alignment, size) == 0)-        return pointer;-    return VMA_NULL;-}-#elif defined(_WIN32)-static void* vma_aligned_alloc(size_t alignment, size_t size)-{-    return _aligned_malloc(size, alignment);-}-#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17-static void* vma_aligned_alloc(size_t alignment, size_t size)-{-    return aligned_alloc(alignment, size);-}-#else-static void* vma_aligned_alloc(size_t alignment, size_t size)-{-    VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system.");-    return VMA_NULL;-}-#endif--#if defined(_WIN32)-static void vma_aligned_free(void* ptr)-{-    _aligned_free(ptr);-}-#else-static void vma_aligned_free(void* VMA_NULLABLE ptr)-{-    free(ptr);-}-#endif--#ifndef VMA_ALIGN_OF-   #define VMA_ALIGN_OF(type)       (alignof(type))-#endif--#ifndef VMA_SYSTEM_ALIGNED_MALLOC-   #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))-#endif--#ifndef VMA_SYSTEM_ALIGNED_FREE-   // VMA_SYSTEM_FREE is the old name, but might have been defined by the user-   #if defined(VMA_SYSTEM_FREE)-      #define VMA_SYSTEM_ALIGNED_FREE(ptr)     VMA_SYSTEM_FREE(ptr)-   #else-      #define VMA_SYSTEM_ALIGNED_FREE(ptr)     vma_aligned_free(ptr)-    #endif-#endif--#ifndef VMA_COUNT_BITS_SET-    // Returns number of bits set to 1 in (v)-    #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v)-#endif--#ifndef VMA_BITSCAN_LSB-    // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX-    #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask)-#endif--#ifndef VMA_BITSCAN_MSB-    // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX-    #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask)-#endif--#ifndef VMA_MIN-   #define VMA_MIN(v1, v2)    ((std::min)((v1), (v2)))-#endif--#ifndef VMA_MAX-   #define VMA_MAX(v1, v2)    ((std::max)((v1), (v2)))-#endif--#ifndef VMA_SWAP-   #define VMA_SWAP(v1, v2)   std::swap((v1), (v2))-#endif--#ifndef VMA_SORT-   #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)-#endif--#ifndef VMA_DEBUG_LOG_FORMAT-   #define VMA_DEBUG_LOG_FORMAT(format, ...)-   /*-   #define VMA_DEBUG_LOG_FORMAT(format, ...) do { \-       printf((format), __VA_ARGS__); \-       printf("\n"); \-   } while(false)-   */-#endif--#ifndef VMA_DEBUG_LOG-    #define VMA_DEBUG_LOG(str)   VMA_DEBUG_LOG_FORMAT("%s", (str))-#endif--#ifndef VMA_CLASS_NO_COPY-    #define VMA_CLASS_NO_COPY(className) \-        private: \-            className(const className&) = delete; \-            className& operator=(const className&) = delete;-#endif-#ifndef VMA_CLASS_NO_COPY_NO_MOVE-    #define VMA_CLASS_NO_COPY_NO_MOVE(className) \-        private: \-            className(const className&) = delete; \-            className(className&&) = delete; \-            className& operator=(const className&) = delete; \-            className& operator=(className&&) = delete;-#endif--// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.-#if VMA_STATS_STRING_ENABLED-    static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)-    {-        snprintf(outStr, strLen, "%u", static_cast<unsigned int>(num));-    }-    static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)-    {-        snprintf(outStr, strLen, "%llu", static_cast<unsigned long long>(num));-    }-    static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)-    {-        snprintf(outStr, strLen, "%p", ptr);-    }-#endif--#ifndef VMA_MUTEX-    class VmaMutex-    {-    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutex)-    public:-        VmaMutex() { }-        void Lock() { m_Mutex.lock(); }-        void Unlock() { m_Mutex.unlock(); }-        bool TryLock() { return m_Mutex.try_lock(); }-    private:-        std::mutex m_Mutex;-    };-    #define VMA_MUTEX VmaMutex-#endif--// Read-write mutex, where "read" is shared access, "write" is exclusive access.-#ifndef VMA_RW_MUTEX-    #if VMA_USE_STL_SHARED_MUTEX-        // Use std::shared_mutex from C++17.-        #include <shared_mutex>-        class VmaRWMutex-        {-        public:-            void LockRead() { m_Mutex.lock_shared(); }-            void UnlockRead() { m_Mutex.unlock_shared(); }-            bool TryLockRead() { return m_Mutex.try_lock_shared(); }-            void LockWrite() { m_Mutex.lock(); }-            void UnlockWrite() { m_Mutex.unlock(); }-            bool TryLockWrite() { return m_Mutex.try_lock(); }-        private:-            std::shared_mutex m_Mutex;-        };-        #define VMA_RW_MUTEX VmaRWMutex-    #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600-        // Use SRWLOCK from WinAPI.-        // Minimum supported client = Windows Vista, server = Windows Server 2008.-        class VmaRWMutex-        {-        public:-            VmaRWMutex() { InitializeSRWLock(&m_Lock); }-            void LockRead() { AcquireSRWLockShared(&m_Lock); }-            void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }-            bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }-            void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }-            void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }-            bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }-        private:-            SRWLOCK m_Lock;-        };-        #define VMA_RW_MUTEX VmaRWMutex-    #else-        // Less efficient fallback: Use normal mutex.-        class VmaRWMutex-        {-        public:-            void LockRead() { m_Mutex.Lock(); }-            void UnlockRead() { m_Mutex.Unlock(); }-            bool TryLockRead() { return m_Mutex.TryLock(); }-            void LockWrite() { m_Mutex.Lock(); }-            void UnlockWrite() { m_Mutex.Unlock(); }-            bool TryLockWrite() { return m_Mutex.TryLock(); }-        private:-            VMA_MUTEX m_Mutex;-        };-        #define VMA_RW_MUTEX VmaRWMutex-    #endif // #if VMA_USE_STL_SHARED_MUTEX-#endif // #ifndef VMA_RW_MUTEX--/*-If providing your own implementation, you need to implement a subset of std::atomic.-*/-#ifndef VMA_ATOMIC_UINT32-    #include <atomic>-    #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>-#endif--#ifndef VMA_ATOMIC_UINT64-    #include <atomic>-    #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>-#endif--#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY-    /**-    Every allocation will have its own memory block.-    Define to 1 for debugging purposes only.-    */-    #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)-#endif--#ifndef VMA_MIN_ALIGNMENT-    /**-    Minimum alignment of all allocations, in bytes.-    Set to more than 1 for debugging purposes. Must be power of two.-    */-    #ifdef VMA_DEBUG_ALIGNMENT // Old name-        #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT-    #else-        #define VMA_MIN_ALIGNMENT (1)-    #endif-#endif--#ifndef VMA_DEBUG_MARGIN-    /**-    Minimum margin after every allocation, in bytes.-    Set nonzero for debugging purposes only.-    */-    #define VMA_DEBUG_MARGIN (0)-#endif--#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS-    /**-    Define this macro to 1 to automatically fill new allocations and destroyed-    allocations with some bit pattern.-    */-    #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)-#endif--#ifndef VMA_DEBUG_DETECT_CORRUPTION-    /**-    Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to-    enable writing magic value to the margin after every allocation and-    validating it, so that memory corruptions (out-of-bounds writes) are detected.-    */-    #define VMA_DEBUG_DETECT_CORRUPTION (0)-#endif--#ifndef VMA_DEBUG_GLOBAL_MUTEX-    /**-    Set this to 1 for debugging purposes only, to enable single mutex protecting all-    entry calls to the library. Can be useful for debugging multithreading issues.-    */-    #define VMA_DEBUG_GLOBAL_MUTEX (0)-#endif--#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY-    /**-    Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.-    Set to more than 1 for debugging purposes only. Must be power of two.-    */-    #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)-#endif--#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT-    /*-    Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount-    and return error instead of leaving up to Vulkan implementation what to do in such cases.-    */-    #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0)-#endif--#ifndef VMA_SMALL_HEAP_MAX_SIZE-   /// Maximum size of a memory heap in Vulkan to consider it "small".-   #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024)-#endif--#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE-   /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.-   #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024)-#endif--/*-Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called-or a persistently mapped allocation is created and destroyed several times in a row.-It keeps additional +1 mapping of a device memory block to prevent calling actual-vkMapMemory/vkUnmapMemory too many times, which may improve performance and help-tools like RenderDoc.-*/-#ifndef VMA_MAPPING_HYSTERESIS_ENABLED-    #define VMA_MAPPING_HYSTERESIS_ENABLED 1-#endif--#define VMA_VALIDATE(cond) do { if(!(cond)) { \-        VMA_ASSERT(0 && "Validation failed: " #cond); \-        return false; \-    } } while(false)--/*******************************************************************************-END OF CONFIGURATION-*/-#endif // _VMA_CONFIGURATION---static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;-static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;-// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.-static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;--// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.-static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;-static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;-static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;-static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;-static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;-static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u;-static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;-static const uint32_t VMA_VENDOR_ID_AMD = 4098;--// This one is tricky. Vulkan specification defines this code as available since-// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131.-// See pull request #207.-#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13)---#if VMA_STATS_STRING_ENABLED-// Correspond to values of enum VmaSuballocationType.-static const char* VMA_SUBALLOCATION_TYPE_NAMES[] =-{-    "FREE",-    "UNKNOWN",-    "BUFFER",-    "IMAGE_UNKNOWN",-    "IMAGE_LINEAR",-    "IMAGE_OPTIMAL",-};-#endif--static VkAllocationCallbacks VmaEmptyAllocationCallbacks =-    { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };---#ifndef _VMA_ENUM_DECLARATIONS--enum VmaSuballocationType-{-    VMA_SUBALLOCATION_TYPE_FREE = 0,-    VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,-    VMA_SUBALLOCATION_TYPE_BUFFER = 2,-    VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,-    VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,-    VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,-    VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF-};--enum VMA_CACHE_OPERATION-{-    VMA_CACHE_FLUSH,-    VMA_CACHE_INVALIDATE-};--enum class VmaAllocationRequestType-{-    Normal,-    TLSF,-    // Used by "Linear" algorithm.-    UpperAddress,-    EndOf1st,-    EndOf2nd,-};--#endif // _VMA_ENUM_DECLARATIONS--#ifndef _VMA_FORWARD_DECLARATIONS-// Opaque handle used by allocation algorithms to identify single allocation in any conforming way.-VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle)--struct VmaMutexLock;-struct VmaMutexLockRead;-struct VmaMutexLockWrite;--template<typename T>-struct AtomicTransactionalIncrement;--template<typename T>-struct VmaStlAllocator;--template<typename T, typename AllocatorT>-class VmaVector;--template<typename T, typename AllocatorT, size_t N>-class VmaSmallVector;--template<typename T>-class VmaPoolAllocator;--template<typename T>-struct VmaListItem;--template<typename T>-class VmaRawList;--template<typename T, typename AllocatorT>-class VmaList;--template<typename ItemTypeTraits>-class VmaIntrusiveLinkedList;--// Unused in this version-#if 0-template<typename T1, typename T2>-struct VmaPair;-template<typename FirstT, typename SecondT>-struct VmaPairFirstLess;--template<typename KeyT, typename ValueT>-class VmaMap;-#endif--#if VMA_STATS_STRING_ENABLED-class VmaStringBuilder;-class VmaJsonWriter;-#endif--class VmaDeviceMemoryBlock;--struct VmaDedicatedAllocationListItemTraits;-class VmaDedicatedAllocationList;--struct VmaSuballocation;-struct VmaSuballocationOffsetLess;-struct VmaSuballocationOffsetGreater;-struct VmaSuballocationItemSizeLess;--typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;--struct VmaAllocationRequest;--class VmaBlockMetadata;-class VmaBlockMetadata_Linear;-class VmaBlockMetadata_TLSF;--class VmaBlockVector;--struct VmaPoolListItemTraits;--struct VmaCurrentBudgetData;--class VmaAllocationObjectAllocator;--#endif // _VMA_FORWARD_DECLARATIONS---#ifndef _VMA_FUNCTIONS--/*-Returns number of bits set to 1 in (v).--On specific platforms and compilers you can use instrinsics like:--Visual Studio:-    return __popcnt(v);-GCC, Clang:-    return static_cast<uint32_t>(__builtin_popcount(v));--Define macro VMA_COUNT_BITS_SET to provide your optimized implementation.-But you need to check in runtime whether user's CPU supports these, as some old processors don't.-*/-static inline uint32_t VmaCountBitsSet(uint32_t v)-{-#if VMA_CPP20-    return std::popcount(v);-#else-    uint32_t c = v - ((v >> 1) & 0x55555555);-    c = ((c >> 2) & 0x33333333) + (c & 0x33333333);-    c = ((c >> 4) + c) & 0x0F0F0F0F;-    c = ((c >> 8) + c) & 0x00FF00FF;-    c = ((c >> 16) + c) & 0x0000FFFF;-    return c;-#endif-}--static inline uint8_t VmaBitScanLSB(uint64_t mask)-{-#if defined(_MSC_VER) && defined(_WIN64)-    unsigned long pos;-    if (_BitScanForward64(&pos, mask))-        return static_cast<uint8_t>(pos);-    return UINT8_MAX;-#elif defined __GNUC__ || defined __clang__-    return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;-#else-    uint8_t pos = 0;-    uint64_t bit = 1;-    do-    {-        if (mask & bit)-            return pos;-        bit <<= 1;-    } while (pos++ < 63);-    return UINT8_MAX;-#endif-}--static inline uint8_t VmaBitScanLSB(uint32_t mask)-{-#ifdef _MSC_VER-    unsigned long pos;-    if (_BitScanForward(&pos, mask))-        return static_cast<uint8_t>(pos);-    return UINT8_MAX;-#elif defined __GNUC__ || defined __clang__-    return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;-#else-    uint8_t pos = 0;-    uint32_t bit = 1;-    do-    {-        if (mask & bit)-            return pos;-        bit <<= 1;-    } while (pos++ < 31);-    return UINT8_MAX;-#endif-}--static inline uint8_t VmaBitScanMSB(uint64_t mask)-{-#if defined(_MSC_VER) && defined(_WIN64)-    unsigned long pos;-    if (_BitScanReverse64(&pos, mask))-        return static_cast<uint8_t>(pos);-#elif defined __GNUC__ || defined __clang__-    if (mask)-        return 63 - static_cast<uint8_t>(__builtin_clzll(mask));-#else-    uint8_t pos = 63;-    uint64_t bit = 1ULL << 63;-    do-    {-        if (mask & bit)-            return pos;-        bit >>= 1;-    } while (pos-- > 0);-#endif-    return UINT8_MAX;-}--static inline uint8_t VmaBitScanMSB(uint32_t mask)-{-#ifdef _MSC_VER-    unsigned long pos;-    if (_BitScanReverse(&pos, mask))-        return static_cast<uint8_t>(pos);-#elif defined __GNUC__ || defined __clang__-    if (mask)-        return 31 - static_cast<uint8_t>(__builtin_clz(mask));-#else-    uint8_t pos = 31;-    uint32_t bit = 1UL << 31;-    do-    {-        if (mask & bit)-            return pos;-        bit >>= 1;-    } while (pos-- > 0);-#endif-    return UINT8_MAX;-}--/*-Returns true if given number is a power of two.-T must be unsigned integer number or signed integer but always nonnegative.-For 0 returns true.-*/-template <typename T>-inline bool VmaIsPow2(T x)-{-    return (x & (x - 1)) == 0;-}--// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.-// Use types like uint32_t, uint64_t as T.-template <typename T>-static inline T VmaAlignUp(T val, T alignment)-{-    VMA_HEAVY_ASSERT(VmaIsPow2(alignment));-    return (val + alignment - 1) & ~(alignment - 1);-}--// Aligns given value down to nearest multiply of align value. For example: VmaAlignDown(11, 8) = 8.-// Use types like uint32_t, uint64_t as T.-template <typename T>-static inline T VmaAlignDown(T val, T alignment)-{-    VMA_HEAVY_ASSERT(VmaIsPow2(alignment));-    return val & ~(alignment - 1);-}--// Division with mathematical rounding to nearest number.-template <typename T>-static inline T VmaRoundDiv(T x, T y)-{-    return (x + (y / (T)2)) / y;-}--// Divide by 'y' and round up to nearest integer.-template <typename T>-static inline T VmaDivideRoundingUp(T x, T y)-{-    return (x + y - (T)1) / y;-}--// Returns smallest power of 2 greater or equal to v.-static inline uint32_t VmaNextPow2(uint32_t v)-{-    v--;-    v |= v >> 1;-    v |= v >> 2;-    v |= v >> 4;-    v |= v >> 8;-    v |= v >> 16;-    v++;-    return v;-}--static inline uint64_t VmaNextPow2(uint64_t v)-{-    v--;-    v |= v >> 1;-    v |= v >> 2;-    v |= v >> 4;-    v |= v >> 8;-    v |= v >> 16;-    v |= v >> 32;-    v++;-    return v;-}--// Returns largest power of 2 less or equal to v.-static inline uint32_t VmaPrevPow2(uint32_t v)-{-    v |= v >> 1;-    v |= v >> 2;-    v |= v >> 4;-    v |= v >> 8;-    v |= v >> 16;-    v = v ^ (v >> 1);-    return v;-}--static inline uint64_t VmaPrevPow2(uint64_t v)-{-    v |= v >> 1;-    v |= v >> 2;-    v |= v >> 4;-    v |= v >> 8;-    v |= v >> 16;-    v |= v >> 32;-    v = v ^ (v >> 1);-    return v;-}--static inline bool VmaStrIsEmpty(const char* pStr)-{-    return pStr == VMA_NULL || *pStr == '\0';-}--/*-Returns true if two memory blocks occupy overlapping pages.-ResourceA must be in less memory offset than ResourceB.--Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"-chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".-*/-static inline bool VmaBlocksOnSamePage(-    VkDeviceSize resourceAOffset,-    VkDeviceSize resourceASize,-    VkDeviceSize resourceBOffset,-    VkDeviceSize pageSize)-{-    VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);-    VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;-    VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);-    VkDeviceSize resourceBStart = resourceBOffset;-    VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);-    return resourceAEndPage == resourceBStartPage;-}--/*-Returns true if given suballocation types could conflict and must respect-VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer-or linear image and another one is optimal image. If type is unknown, behave-conservatively.-*/-static inline bool VmaIsBufferImageGranularityConflict(-    VmaSuballocationType suballocType1,-    VmaSuballocationType suballocType2)-{-    if (suballocType1 > suballocType2)-    {-        VMA_SWAP(suballocType1, suballocType2);-    }--    switch (suballocType1)-    {-    case VMA_SUBALLOCATION_TYPE_FREE:-        return false;-    case VMA_SUBALLOCATION_TYPE_UNKNOWN:-        return true;-    case VMA_SUBALLOCATION_TYPE_BUFFER:-        return-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;-    case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:-        return-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;-    case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:-        return-            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;-    case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:-        return false;-    default:-        VMA_ASSERT(0);-        return true;-    }-}--static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)-{-#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION-    uint32_t* pDst = (uint32_t*)((char*)pData + offset);-    const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);-    for (size_t i = 0; i < numberCount; ++i, ++pDst)-    {-        *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;-    }-#else-    // no-op-#endif-}--static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)-{-#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION-    const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);-    const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);-    for (size_t i = 0; i < numberCount; ++i, ++pSrc)-    {-        if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)-        {-            return false;-        }-    }-#endif-    return true;-}--/*-Fills structure with parameters of an example buffer to be used for transfers-during GPU memory defragmentation.-*/-static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)-{-    memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));-    outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;-    outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;-    outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.-}---/*-Performs binary search and returns iterator to first element that is greater or-equal to (key), according to comparison (cmp).--Cmp should return true if first argument is less than second argument.--Returned value is the found element, if present in the collection or place where-new element with value (key) should be inserted.-*/-template <typename CmpLess, typename IterT, typename KeyT>-static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)-{-    size_t down = 0, up = size_t(end - beg);-    while (down < up)-    {-        const size_t mid = down + (up - down) / 2;  // Overflow-safe midpoint calculation-        if (cmp(*(beg + mid), key))-        {-            down = mid + 1;-        }-        else-        {-            up = mid;-        }-    }-    return beg + down;-}--template<typename CmpLess, typename IterT, typename KeyT>-IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)-{-    IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(-        beg, end, value, cmp);-    if (it == end ||-        (!cmp(*it, value) && !cmp(value, *it)))-    {-        return it;-    }-    return end;-}--/*-Returns true if all pointers in the array are not-null and unique.-Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.-T must be pointer type, e.g. VmaAllocation, VmaPool.-*/-template<typename T>-static bool VmaValidatePointerArray(uint32_t count, const T* arr)-{-    for (uint32_t i = 0; i < count; ++i)-    {-        const T iPtr = arr[i];-        if (iPtr == VMA_NULL)-        {-            return false;-        }-        for (uint32_t j = i + 1; j < count; ++j)-        {-            if (iPtr == arr[j])-            {-                return false;-            }-        }-    }-    return true;-}--template<typename MainT, typename NewT>-static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)-{-    newStruct->pNext = mainStruct->pNext;-    mainStruct->pNext = newStruct;-}--// This is the main algorithm that guides the selection of a memory type best for an allocation --// converts usage to required/preferred/not preferred flags.-static bool FindMemoryPreferences(-    bool isIntegratedGPU,-    const VmaAllocationCreateInfo& allocCreateInfo,-    VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.-    VkMemoryPropertyFlags& outRequiredFlags,-    VkMemoryPropertyFlags& outPreferredFlags,-    VkMemoryPropertyFlags& outNotPreferredFlags)-{-    outRequiredFlags = allocCreateInfo.requiredFlags;-    outPreferredFlags = allocCreateInfo.preferredFlags;-    outNotPreferredFlags = 0;--    switch(allocCreateInfo.usage)-    {-    case VMA_MEMORY_USAGE_UNKNOWN:-        break;-    case VMA_MEMORY_USAGE_GPU_ONLY:-        if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)-        {-            outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-        }-        break;-    case VMA_MEMORY_USAGE_CPU_ONLY:-        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;-        break;-    case VMA_MEMORY_USAGE_CPU_TO_GPU:-        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-        if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)-        {-            outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-        }-        break;-    case VMA_MEMORY_USAGE_GPU_TO_CPU:-        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-        outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;-        break;-    case VMA_MEMORY_USAGE_CPU_COPY:-        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-        break;-    case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:-        outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;-        break;-    case VMA_MEMORY_USAGE_AUTO:-    case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:-    case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:-    {-        if(bufImgUsage == UINT32_MAX)-        {-            VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known.");-            return false;-        }-        // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same VK_BUFFER_IMAGE_TRANSFER*.-        const bool deviceAccess = (bufImgUsage & ~static_cast<VkFlags>(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;-        const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;-        const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;-        const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;-        const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;-        const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;--        // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU.-        if(hostAccessRandom)-        {-            // Prefer cached. Cannot require it, because some platforms don't have it (e.g. Raspberry Pi - see #362)!-            outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;--            if (!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)-            {-                // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL.-                // Omitting HOST_VISIBLE here is intentional.-                // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one.-                // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list.-                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-            }-            else-            {-                // Always CPU memory.-                outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-            }-        }-        // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined.-        else if(hostAccessSequentialWrite)-        {-            // Want uncached and write-combined.-            outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;--            if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)-            {-                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-            }-            else-            {-                outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-                // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame)-                if(deviceAccess)-                {-                    // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory.-                    if(preferHost)-                        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-                    else-                        outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-                }-                // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU)-                else-                {-                    // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory.-                    if(preferDevice)-                        outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-                    else-                        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-                }-            }-        }-        // No CPU access-        else-        {-            // if(deviceAccess)-            //-            // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory,-            // unless there is a clear preference from the user not to do so.-            //-            // else:-            //-            // No direct GPU access, no CPU access, just transfers.-            // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or-            // a "swap file" copy to free some GPU memory (then better CPU memory).-            // Up to the user to decide. If no preferece, assume the former and choose GPU memory.--            if(preferHost)-                outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-            else-                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;-        }-        break;-    }-    default:-        VMA_ASSERT(0);-    }--    // Avoid DEVICE_COHERENT unless explicitly requested.-    if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &-        (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)-    {-        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;-    }--    return true;-}--////////////////////////////////////////////////////////////////////////////////-// Memory allocation--static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)-{-    void* result = VMA_NULL;-    if ((pAllocationCallbacks != VMA_NULL) &&-        (pAllocationCallbacks->pfnAllocation != VMA_NULL))-    {-        result = (*pAllocationCallbacks->pfnAllocation)(-            pAllocationCallbacks->pUserData,-            size,-            alignment,-            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);-    }-    else-    {-        result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);-    }-    VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");-    return result;-}--static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)-{-    if ((pAllocationCallbacks != VMA_NULL) &&-        (pAllocationCallbacks->pfnFree != VMA_NULL))-    {-        (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);-    }-    else-    {-        VMA_SYSTEM_ALIGNED_FREE(ptr);-    }-}--template<typename T>-static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)-{-    return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));-}--template<typename T>-static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)-{-    return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));-}--#define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)--#define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)--template<typename T>-static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)-{-    ptr->~T();-    VmaFree(pAllocationCallbacks, ptr);-}--template<typename T>-static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)-{-    if (ptr != VMA_NULL)-    {-        for (size_t i = count; i--; )-        {-            ptr[i].~T();-        }-        VmaFree(pAllocationCallbacks, ptr);-    }-}--static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)-{-    if (srcStr != VMA_NULL)-    {-        const size_t len = strlen(srcStr);-        char* const result = vma_new_array(allocs, char, len + 1);-        memcpy(result, srcStr, len + 1);-        return result;-    }-    return VMA_NULL;-}--#if VMA_STATS_STRING_ENABLED-static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)-{-    if (srcStr != VMA_NULL)-    {-        char* const result = vma_new_array(allocs, char, strLen + 1);-        memcpy(result, srcStr, strLen);-        result[strLen] = '\0';-        return result;-    }-    return VMA_NULL;-}-#endif // VMA_STATS_STRING_ENABLED--static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)-{-    if (str != VMA_NULL)-    {-        const size_t len = strlen(str);-        vma_delete_array(allocs, str, len + 1);-    }-}--template<typename CmpLess, typename VectorT>-size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)-{-    const size_t indexToInsert = VmaBinaryFindFirstNotLess(-        vector.data(),-        vector.data() + vector.size(),-        value,-        CmpLess()) - vector.data();-    VmaVectorInsert(vector, indexToInsert, value);-    return indexToInsert;-}--template<typename CmpLess, typename VectorT>-bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)-{-    CmpLess comparator;-    typename VectorT::iterator it = VmaBinaryFindFirstNotLess(-        vector.begin(),-        vector.end(),-        value,-        comparator);-    if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))-    {-        size_t indexToRemove = it - vector.begin();-        VmaVectorRemove(vector, indexToRemove);-        return true;-    }-    return false;-}-#endif // _VMA_FUNCTIONS--#ifndef _VMA_STATISTICS_FUNCTIONS--static void VmaClearStatistics(VmaStatistics& outStats)-{-    outStats.blockCount = 0;-    outStats.allocationCount = 0;-    outStats.blockBytes = 0;-    outStats.allocationBytes = 0;-}--static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)-{-    inoutStats.blockCount += src.blockCount;-    inoutStats.allocationCount += src.allocationCount;-    inoutStats.blockBytes += src.blockBytes;-    inoutStats.allocationBytes += src.allocationBytes;-}--static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)-{-    VmaClearStatistics(outStats.statistics);-    outStats.unusedRangeCount = 0;-    outStats.allocationSizeMin = VK_WHOLE_SIZE;-    outStats.allocationSizeMax = 0;-    outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;-    outStats.unusedRangeSizeMax = 0;-}--static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)-{-    inoutStats.statistics.allocationCount++;-    inoutStats.statistics.allocationBytes += size;-    inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);-    inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);-}--static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)-{-    inoutStats.unusedRangeCount++;-    inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);-    inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);-}--static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)-{-    VmaAddStatistics(inoutStats.statistics, src.statistics);-    inoutStats.unusedRangeCount += src.unusedRangeCount;-    inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);-    inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);-    inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);-    inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);-}--#endif // _VMA_STATISTICS_FUNCTIONS--#ifndef _VMA_MUTEX_LOCK-// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).-struct VmaMutexLock-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLock)-public:-    VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :-        m_pMutex(useMutex ? &mutex : VMA_NULL)-    {-        if (m_pMutex) { m_pMutex->Lock(); }-    }-    ~VmaMutexLock() {  if (m_pMutex) { m_pMutex->Unlock(); } }--private:-    VMA_MUTEX* m_pMutex;-};--// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.-struct VmaMutexLockRead-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockRead)-public:-    VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :-        m_pMutex(useMutex ? &mutex : VMA_NULL)-    {-        if (m_pMutex) { m_pMutex->LockRead(); }-    }-    ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }--private:-    VMA_RW_MUTEX* m_pMutex;-};--// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.-struct VmaMutexLockWrite-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockWrite)-public:-    VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)-        : m_pMutex(useMutex ? &mutex : VMA_NULL)-    {-        if (m_pMutex) { m_pMutex->LockWrite(); }-    }-    ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }--private:-    VMA_RW_MUTEX* m_pMutex;-};--#if VMA_DEBUG_GLOBAL_MUTEX-    static VMA_MUTEX gDebugGlobalMutex;-    #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);-#else-    #define VMA_DEBUG_GLOBAL_MUTEX_LOCK-#endif-#endif // _VMA_MUTEX_LOCK--#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT-// An object that increments given atomic but decrements it back in the destructor unless Commit() is called.-template<typename AtomicT>-struct AtomicTransactionalIncrement-{-public:-    using T = decltype(AtomicT().load());--    ~AtomicTransactionalIncrement()-    {-        if(m_Atomic)-            --(*m_Atomic);-    }--    void Commit() { m_Atomic = nullptr; }-    T Increment(AtomicT* atomic)-    {-        m_Atomic = atomic;-        return m_Atomic->fetch_add(1);-    }--private:-    AtomicT* m_Atomic = nullptr;-};-#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT--#ifndef _VMA_STL_ALLOCATOR-// STL-compatible allocator.-template<typename T>-struct VmaStlAllocator-{-    const VkAllocationCallbacks* const m_pCallbacks;-    typedef T value_type;--    VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}-    template<typename U>-    VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}-    VmaStlAllocator(const VmaStlAllocator&) = default;-    VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;--    T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }-    void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }--    template<typename U>-    bool operator==(const VmaStlAllocator<U>& rhs) const-    {-        return m_pCallbacks == rhs.m_pCallbacks;-    }-    template<typename U>-    bool operator!=(const VmaStlAllocator<U>& rhs) const-    {-        return m_pCallbacks != rhs.m_pCallbacks;-    }-};-#endif // _VMA_STL_ALLOCATOR--#ifndef _VMA_VECTOR-/* Class with interface compatible with subset of std::vector.-T must be POD because constructors and destructors are not called and memcpy is-used for these objects. */-template<typename T, typename AllocatorT>-class VmaVector-{-public:-    typedef T value_type;-    typedef T* iterator;-    typedef const T* const_iterator;--    VmaVector(const AllocatorT& allocator);-    VmaVector(size_t count, const AllocatorT& allocator);-    // This version of the constructor is here for compatibility with pre-C++14 std::vector.-    // value is unused.-    VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}-    VmaVector(const VmaVector<T, AllocatorT>& src);-    VmaVector& operator=(const VmaVector& rhs);-    ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }--    bool empty() const { return m_Count == 0; }-    size_t size() const { return m_Count; }-    T* data() { return m_pArray; }-    T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }-    T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }-    const T* data() const { return m_pArray; }-    const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }-    const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }--    iterator begin() { return m_pArray; }-    iterator end() { return m_pArray + m_Count; }-    const_iterator cbegin() const { return m_pArray; }-    const_iterator cend() const { return m_pArray + m_Count; }-    const_iterator begin() const { return cbegin(); }-    const_iterator end() const { return cend(); }--    void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }-    void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }-    void push_front(const T& src) { insert(0, src); }--    void push_back(const T& src);-    void reserve(size_t newCapacity, bool freeMemory = false);-    void resize(size_t newCount);-    void clear() { resize(0); }-    void shrink_to_fit();-    void insert(size_t index, const T& src);-    void remove(size_t index);--    T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }-    const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }--private:-    AllocatorT m_Allocator;-    T* m_pArray;-    size_t m_Count;-    size_t m_Capacity;-};--#ifndef _VMA_VECTOR_FUNCTIONS-template<typename T, typename AllocatorT>-VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)-    : m_Allocator(allocator),-    m_pArray(VMA_NULL),-    m_Count(0),-    m_Capacity(0) {}--template<typename T, typename AllocatorT>-VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)-    : m_Allocator(allocator),-    m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),-    m_Count(count),-    m_Capacity(count) {}--template<typename T, typename AllocatorT>-VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)-    : m_Allocator(src.m_Allocator),-    m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),-    m_Count(src.m_Count),-    m_Capacity(src.m_Count)-{-    if (m_Count != 0)-    {-        memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));-    }-}--template<typename T, typename AllocatorT>-VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)-{-    if (&rhs != this)-    {-        resize(rhs.m_Count);-        if (m_Count != 0)-        {-            memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));-        }-    }-    return *this;-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::push_back(const T& src)-{-    const size_t newIndex = size();-    resize(newIndex + 1);-    m_pArray[newIndex] = src;-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)-{-    newCapacity = VMA_MAX(newCapacity, m_Count);--    if ((newCapacity < m_Capacity) && !freeMemory)-    {-        newCapacity = m_Capacity;-    }--    if (newCapacity != m_Capacity)-    {-        T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;-        if (m_Count != 0)-        {-            memcpy(newArray, m_pArray, m_Count * sizeof(T));-        }-        VmaFree(m_Allocator.m_pCallbacks, m_pArray);-        m_Capacity = newCapacity;-        m_pArray = newArray;-    }-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::resize(size_t newCount)-{-    size_t newCapacity = m_Capacity;-    if (newCount > m_Capacity)-    {-        newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));-    }--    if (newCapacity != m_Capacity)-    {-        T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;-        const size_t elementsToCopy = VMA_MIN(m_Count, newCount);-        if (elementsToCopy != 0)-        {-            memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));-        }-        VmaFree(m_Allocator.m_pCallbacks, m_pArray);-        m_Capacity = newCapacity;-        m_pArray = newArray;-    }--    m_Count = newCount;-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::shrink_to_fit()-{-    if (m_Capacity > m_Count)-    {-        T* newArray = VMA_NULL;-        if (m_Count > 0)-        {-            newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);-            memcpy(newArray, m_pArray, m_Count * sizeof(T));-        }-        VmaFree(m_Allocator.m_pCallbacks, m_pArray);-        m_Capacity = m_Count;-        m_pArray = newArray;-    }-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)-{-    VMA_HEAVY_ASSERT(index <= m_Count);-    const size_t oldCount = size();-    resize(oldCount + 1);-    if (index < oldCount)-    {-        memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));-    }-    m_pArray[index] = src;-}--template<typename T, typename AllocatorT>-void VmaVector<T, AllocatorT>::remove(size_t index)-{-    VMA_HEAVY_ASSERT(index < m_Count);-    const size_t oldCount = size();-    if (index < oldCount - 1)-    {-        memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));-    }-    resize(oldCount - 1);-}-#endif // _VMA_VECTOR_FUNCTIONS--template<typename T, typename allocatorT>-static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)-{-    vec.insert(index, item);-}--template<typename T, typename allocatorT>-static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)-{-    vec.remove(index);-}-#endif // _VMA_VECTOR--#ifndef _VMA_SMALL_VECTOR-/*-This is a vector (a variable-sized array), optimized for the case when the array is small.--It contains some number of elements in-place, which allows it to avoid heap allocation-when the actual number of elements is below that threshold. This allows normal "small"-cases to be fast without losing generality for large inputs.-*/-template<typename T, typename AllocatorT, size_t N>-class VmaSmallVector-{-public:-    typedef T value_type;-    typedef T* iterator;--    VmaSmallVector(const AllocatorT& allocator);-    VmaSmallVector(size_t count, const AllocatorT& allocator);-    template<typename SrcT, typename SrcAllocatorT, size_t SrcN>-    VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;-    template<typename SrcT, typename SrcAllocatorT, size_t SrcN>-    VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;-    ~VmaSmallVector() = default;--    bool empty() const { return m_Count == 0; }-    size_t size() const { return m_Count; }-    T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }-    T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }-    T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }-    const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }-    const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }-    const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }--    iterator begin() { return data(); }-    iterator end() { return data() + m_Count; }--    void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }-    void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }-    void push_front(const T& src) { insert(0, src); }--    void push_back(const T& src);-    void resize(size_t newCount, bool freeMemory = false);-    void clear(bool freeMemory = false);-    void insert(size_t index, const T& src);-    void remove(size_t index);--    T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }-    const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }--private:-    size_t m_Count;-    T m_StaticArray[N]; // Used when m_Size <= N-    VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N-};--#ifndef _VMA_SMALL_VECTOR_FUNCTIONS-template<typename T, typename AllocatorT, size_t N>-VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)-    : m_Count(0),-    m_DynamicArray(allocator) {}--template<typename T, typename AllocatorT, size_t N>-VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)-    : m_Count(count),-    m_DynamicArray(count > N ? count : 0, allocator) {}--template<typename T, typename AllocatorT, size_t N>-void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)-{-    const size_t newIndex = size();-    resize(newIndex + 1);-    data()[newIndex] = src;-}--template<typename T, typename AllocatorT, size_t N>-void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)-{-    if (newCount > N && m_Count > N)-    {-        // Any direction, staying in m_DynamicArray-        m_DynamicArray.resize(newCount);-        if (freeMemory)-        {-            m_DynamicArray.shrink_to_fit();-        }-    }-    else if (newCount > N && m_Count <= N)-    {-        // Growing, moving from m_StaticArray to m_DynamicArray-        m_DynamicArray.resize(newCount);-        if (m_Count > 0)-        {-            memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));-        }-    }-    else if (newCount <= N && m_Count > N)-    {-        // Shrinking, moving from m_DynamicArray to m_StaticArray-        if (newCount > 0)-        {-            memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));-        }-        m_DynamicArray.resize(0);-        if (freeMemory)-        {-            m_DynamicArray.shrink_to_fit();-        }-    }-    else-    {-        // Any direction, staying in m_StaticArray - nothing to do here-    }-    m_Count = newCount;-}--template<typename T, typename AllocatorT, size_t N>-void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)-{-    m_DynamicArray.clear();-    if (freeMemory)-    {-        m_DynamicArray.shrink_to_fit();-    }-    m_Count = 0;-}--template<typename T, typename AllocatorT, size_t N>-void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)-{-    VMA_HEAVY_ASSERT(index <= m_Count);-    const size_t oldCount = size();-    resize(oldCount + 1);-    T* const dataPtr = data();-    if (index < oldCount)-    {-        //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.-        memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));-    }-    dataPtr[index] = src;-}--template<typename T, typename AllocatorT, size_t N>-void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)-{-    VMA_HEAVY_ASSERT(index < m_Count);-    const size_t oldCount = size();-    if (index < oldCount - 1)-    {-        //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.-        T* const dataPtr = data();-        memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));-    }-    resize(oldCount - 1);-}-#endif // _VMA_SMALL_VECTOR_FUNCTIONS-#endif // _VMA_SMALL_VECTOR--#ifndef _VMA_POOL_ALLOCATOR-/*-Allocator for objects of type T using a list of arrays (pools) to speed up-allocation. Number of elements that can be allocated is not bounded because-allocator can create multiple blocks.-*/-template<typename T>-class VmaPoolAllocator-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaPoolAllocator)-public:-    VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);-    ~VmaPoolAllocator();-    template<typename... Types> T* Alloc(Types&&... args);-    void Free(T* ptr);--private:-    union Item-    {-        uint32_t NextFreeIndex;-        alignas(T) char Value[sizeof(T)];-    };-    struct ItemBlock-    {-        Item* pItems;-        uint32_t Capacity;-        uint32_t FirstFreeIndex;-    };--    const VkAllocationCallbacks* m_pAllocationCallbacks;-    const uint32_t m_FirstBlockCapacity;-    VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;--    ItemBlock& CreateNewBlock();-};--#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS-template<typename T>-VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)-    : m_pAllocationCallbacks(pAllocationCallbacks),-    m_FirstBlockCapacity(firstBlockCapacity),-    m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))-{-    VMA_ASSERT(m_FirstBlockCapacity > 1);-}--template<typename T>-VmaPoolAllocator<T>::~VmaPoolAllocator()-{-    for (size_t i = m_ItemBlocks.size(); i--;)-        vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);-    m_ItemBlocks.clear();-}--template<typename T>-template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)-{-    for (size_t i = m_ItemBlocks.size(); i--; )-    {-        ItemBlock& block = m_ItemBlocks[i];-        // This block has some free items: Use first one.-        if (block.FirstFreeIndex != UINT32_MAX)-        {-            Item* const pItem = &block.pItems[block.FirstFreeIndex];-            block.FirstFreeIndex = pItem->NextFreeIndex;-            T* result = (T*)&pItem->Value;-            new(result)T(std::forward<Types>(args)...); // Explicit constructor call.-            return result;-        }-    }--    // No block has free item: Create new one and use it.-    ItemBlock& newBlock = CreateNewBlock();-    Item* const pItem = &newBlock.pItems[0];-    newBlock.FirstFreeIndex = pItem->NextFreeIndex;-    T* result = (T*)&pItem->Value;-    new(result) T(std::forward<Types>(args)...); // Explicit constructor call.-    return result;-}--template<typename T>-void VmaPoolAllocator<T>::Free(T* ptr)-{-    // Search all memory blocks to find ptr.-    for (size_t i = m_ItemBlocks.size(); i--; )-    {-        ItemBlock& block = m_ItemBlocks[i];--        // Casting to union.-        Item* pItemPtr;-        memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));--        // Check if pItemPtr is in address range of this block.-        if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))-        {-            ptr->~T(); // Explicit destructor call.-            const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);-            pItemPtr->NextFreeIndex = block.FirstFreeIndex;-            block.FirstFreeIndex = index;-            return;-        }-    }-    VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");-}--template<typename T>-typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()-{-    const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?-        m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;--    const ItemBlock newBlock =-    {-        vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),-        newBlockCapacity,-        0-    };--    m_ItemBlocks.push_back(newBlock);--    // Setup singly-linked list of all free items in this block.-    for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)-        newBlock.pItems[i].NextFreeIndex = i + 1;-    newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;-    return m_ItemBlocks.back();-}-#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS-#endif // _VMA_POOL_ALLOCATOR--#ifndef _VMA_RAW_LIST-template<typename T>-struct VmaListItem-{-    VmaListItem* pPrev;-    VmaListItem* pNext;-    T Value;-};--// Doubly linked list.-template<typename T>-class VmaRawList-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaRawList)-public:-    typedef VmaListItem<T> ItemType;--    VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);-    // Intentionally not calling Clear, because that would be unnecessary-    // computations to return all items to m_ItemAllocator as free.-    ~VmaRawList() = default;--    size_t GetCount() const { return m_Count; }-    bool IsEmpty() const { return m_Count == 0; }--    ItemType* Front() { return m_pFront; }-    ItemType* Back() { return m_pBack; }-    const ItemType* Front() const { return m_pFront; }-    const ItemType* Back() const { return m_pBack; }--    ItemType* PushFront();-    ItemType* PushBack();-    ItemType* PushFront(const T& value);-    ItemType* PushBack(const T& value);-    void PopFront();-    void PopBack();--    // Item can be null - it means PushBack.-    ItemType* InsertBefore(ItemType* pItem);-    // Item can be null - it means PushFront.-    ItemType* InsertAfter(ItemType* pItem);-    ItemType* InsertBefore(ItemType* pItem, const T& value);-    ItemType* InsertAfter(ItemType* pItem, const T& value);--    void Clear();-    void Remove(ItemType* pItem);--private:-    const VkAllocationCallbacks* const m_pAllocationCallbacks;-    VmaPoolAllocator<ItemType> m_ItemAllocator;-    ItemType* m_pFront;-    ItemType* m_pBack;-    size_t m_Count;-};--#ifndef _VMA_RAW_LIST_FUNCTIONS-template<typename T>-VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)-    : m_pAllocationCallbacks(pAllocationCallbacks),-    m_ItemAllocator(pAllocationCallbacks, 128),-    m_pFront(VMA_NULL),-    m_pBack(VMA_NULL),-    m_Count(0) {}--template<typename T>-VmaListItem<T>* VmaRawList<T>::PushFront()-{-    ItemType* const pNewItem = m_ItemAllocator.Alloc();-    pNewItem->pPrev = VMA_NULL;-    if (IsEmpty())-    {-        pNewItem->pNext = VMA_NULL;-        m_pFront = pNewItem;-        m_pBack = pNewItem;-        m_Count = 1;-    }-    else-    {-        pNewItem->pNext = m_pFront;-        m_pFront->pPrev = pNewItem;-        m_pFront = pNewItem;-        ++m_Count;-    }-    return pNewItem;-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::PushBack()-{-    ItemType* const pNewItem = m_ItemAllocator.Alloc();-    pNewItem->pNext = VMA_NULL;-    if(IsEmpty())-    {-        pNewItem->pPrev = VMA_NULL;-        m_pFront = pNewItem;-        m_pBack = pNewItem;-        m_Count = 1;-    }-    else-    {-        pNewItem->pPrev = m_pBack;-        m_pBack->pNext = pNewItem;-        m_pBack = pNewItem;-        ++m_Count;-    }-    return pNewItem;-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)-{-    ItemType* const pNewItem = PushFront();-    pNewItem->Value = value;-    return pNewItem;-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)-{-    ItemType* const pNewItem = PushBack();-    pNewItem->Value = value;-    return pNewItem;-}--template<typename T>-void VmaRawList<T>::PopFront()-{-    VMA_HEAVY_ASSERT(m_Count > 0);-    ItemType* const pFrontItem = m_pFront;-    ItemType* const pNextItem = pFrontItem->pNext;-    if (pNextItem != VMA_NULL)-    {-        pNextItem->pPrev = VMA_NULL;-    }-    m_pFront = pNextItem;-    m_ItemAllocator.Free(pFrontItem);-    --m_Count;-}--template<typename T>-void VmaRawList<T>::PopBack()-{-    VMA_HEAVY_ASSERT(m_Count > 0);-    ItemType* const pBackItem = m_pBack;-    ItemType* const pPrevItem = pBackItem->pPrev;-    if(pPrevItem != VMA_NULL)-    {-        pPrevItem->pNext = VMA_NULL;-    }-    m_pBack = pPrevItem;-    m_ItemAllocator.Free(pBackItem);-    --m_Count;-}--template<typename T>-void VmaRawList<T>::Clear()-{-    if (IsEmpty() == false)-    {-        ItemType* pItem = m_pBack;-        while (pItem != VMA_NULL)-        {-            ItemType* const pPrevItem = pItem->pPrev;-            m_ItemAllocator.Free(pItem);-            pItem = pPrevItem;-        }-        m_pFront = VMA_NULL;-        m_pBack = VMA_NULL;-        m_Count = 0;-    }-}--template<typename T>-void VmaRawList<T>::Remove(ItemType* pItem)-{-    VMA_HEAVY_ASSERT(pItem != VMA_NULL);-    VMA_HEAVY_ASSERT(m_Count > 0);--    if(pItem->pPrev != VMA_NULL)-    {-        pItem->pPrev->pNext = pItem->pNext;-    }-    else-    {-        VMA_HEAVY_ASSERT(m_pFront == pItem);-        m_pFront = pItem->pNext;-    }--    if(pItem->pNext != VMA_NULL)-    {-        pItem->pNext->pPrev = pItem->pPrev;-    }-    else-    {-        VMA_HEAVY_ASSERT(m_pBack == pItem);-        m_pBack = pItem->pPrev;-    }--    m_ItemAllocator.Free(pItem);-    --m_Count;-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)-{-    if(pItem != VMA_NULL)-    {-        ItemType* const prevItem = pItem->pPrev;-        ItemType* const newItem = m_ItemAllocator.Alloc();-        newItem->pPrev = prevItem;-        newItem->pNext = pItem;-        pItem->pPrev = newItem;-        if(prevItem != VMA_NULL)-        {-            prevItem->pNext = newItem;-        }-        else-        {-            VMA_HEAVY_ASSERT(m_pFront == pItem);-            m_pFront = newItem;-        }-        ++m_Count;-        return newItem;-    }-    else-        return PushBack();-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)-{-    if(pItem != VMA_NULL)-    {-        ItemType* const nextItem = pItem->pNext;-        ItemType* const newItem = m_ItemAllocator.Alloc();-        newItem->pNext = nextItem;-        newItem->pPrev = pItem;-        pItem->pNext = newItem;-        if(nextItem != VMA_NULL)-        {-            nextItem->pPrev = newItem;-        }-        else-        {-            VMA_HEAVY_ASSERT(m_pBack == pItem);-            m_pBack = newItem;-        }-        ++m_Count;-        return newItem;-    }-    else-        return PushFront();-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)-{-    ItemType* const newItem = InsertBefore(pItem);-    newItem->Value = value;-    return newItem;-}--template<typename T>-VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)-{-    ItemType* const newItem = InsertAfter(pItem);-    newItem->Value = value;-    return newItem;-}-#endif // _VMA_RAW_LIST_FUNCTIONS-#endif // _VMA_RAW_LIST--#ifndef _VMA_LIST-template<typename T, typename AllocatorT>-class VmaList-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaList)-public:-    class reverse_iterator;-    class const_iterator;-    class const_reverse_iterator;--    class iterator-    {-        friend class const_iterator;-        friend class VmaList<T, AllocatorT>;-    public:-        iterator() :  m_pList(VMA_NULL), m_pItem(VMA_NULL) {}-        iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}--        T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }-        T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }--        bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }-        bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }--        iterator operator++(int) { iterator result = *this; ++*this; return result; }-        iterator operator--(int) { iterator result = *this; --*this; return result; }--        iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }-        iterator& operator--();--    private:-        VmaRawList<T>* m_pList;-        VmaListItem<T>* m_pItem;--        iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}-    };-    class reverse_iterator-    {-        friend class const_reverse_iterator;-        friend class VmaList<T, AllocatorT>;-    public:-        reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}-        reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}--        T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }-        T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }--        bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }-        bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }--        reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }-        reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }--        reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }-        reverse_iterator& operator--();--    private:-        VmaRawList<T>* m_pList;-        VmaListItem<T>* m_pItem;--        reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}-    };-    class const_iterator-    {-        friend class VmaList<T, AllocatorT>;-    public:-        const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}-        const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}-        const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}--        iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }--        const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }-        const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }--        bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }-        bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }--        const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }-        const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }--        const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }-        const_iterator& operator--();--    private:-        const VmaRawList<T>* m_pList;-        const VmaListItem<T>* m_pItem;--        const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}-    };-    class const_reverse_iterator-    {-        friend class VmaList<T, AllocatorT>;-    public:-        const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}-        const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}-        const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}--        reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }--        const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }-        const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }--        bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }-        bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }--        const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }-        const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }--        const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }-        const_reverse_iterator& operator--();--    private:-        const VmaRawList<T>* m_pList;-        const VmaListItem<T>* m_pItem;--        const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}-    };--    VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}--    bool empty() const { return m_RawList.IsEmpty(); }-    size_t size() const { return m_RawList.GetCount(); }--    iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }-    iterator end() { return iterator(&m_RawList, VMA_NULL); }--    const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }-    const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }--    const_iterator begin() const { return cbegin(); }-    const_iterator end() const { return cend(); }--    reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }-    reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }--    const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }-    const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }--    const_reverse_iterator rbegin() const { return crbegin(); }-    const_reverse_iterator rend() const { return crend(); }--    void push_back(const T& value) { m_RawList.PushBack(value); }-    iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }--    void clear() { m_RawList.Clear(); }-    void erase(iterator it) { m_RawList.Remove(it.m_pItem); }--private:-    VmaRawList<T> m_RawList;-};--#ifndef _VMA_LIST_FUNCTIONS-template<typename T, typename AllocatorT>-typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()-{-    if (m_pItem != VMA_NULL)-    {-        m_pItem = m_pItem->pPrev;-    }-    else-    {-        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());-        m_pItem = m_pList->Back();-    }-    return *this;-}--template<typename T, typename AllocatorT>-typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()-{-    if (m_pItem != VMA_NULL)-    {-        m_pItem = m_pItem->pNext;-    }-    else-    {-        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());-        m_pItem = m_pList->Front();-    }-    return *this;-}--template<typename T, typename AllocatorT>-typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()-{-    if (m_pItem != VMA_NULL)-    {-        m_pItem = m_pItem->pPrev;-    }-    else-    {-        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());-        m_pItem = m_pList->Back();-    }-    return *this;-}--template<typename T, typename AllocatorT>-typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()-{-    if (m_pItem != VMA_NULL)-    {-        m_pItem = m_pItem->pNext;-    }-    else-    {-        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());-        m_pItem = m_pList->Back();-    }-    return *this;-}-#endif // _VMA_LIST_FUNCTIONS-#endif // _VMA_LIST--#ifndef _VMA_INTRUSIVE_LINKED_LIST-/*-Expected interface of ItemTypeTraits:-struct MyItemTypeTraits-{-    typedef MyItem ItemType;-    static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }-    static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }-    static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }-    static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }-};-*/-template<typename ItemTypeTraits>-class VmaIntrusiveLinkedList-{-public:-    typedef typename ItemTypeTraits::ItemType ItemType;-    static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }-    static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }--    // Movable, not copyable.-    VmaIntrusiveLinkedList() = default;-    VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src);-    VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;-    VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src);-    VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;-    ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }--    size_t GetCount() const { return m_Count; }-    bool IsEmpty() const { return m_Count == 0; }-    ItemType* Front() { return m_Front; }-    ItemType* Back() { return m_Back; }-    const ItemType* Front() const { return m_Front; }-    const ItemType* Back() const { return m_Back; }--    void PushBack(ItemType* item);-    void PushFront(ItemType* item);-    ItemType* PopBack();-    ItemType* PopFront();--    // MyItem can be null - it means PushBack.-    void InsertBefore(ItemType* existingItem, ItemType* newItem);-    // MyItem can be null - it means PushFront.-    void InsertAfter(ItemType* existingItem, ItemType* newItem);-    void Remove(ItemType* item);-    void RemoveAll();--private:-    ItemType* m_Front = VMA_NULL;-    ItemType* m_Back = VMA_NULL;-    size_t m_Count = 0;-};--#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS-template<typename ItemTypeTraits>-VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src)-    : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)-{-    src.m_Front = src.m_Back = VMA_NULL;-    src.m_Count = 0;-}--template<typename ItemTypeTraits>-VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src)-{-    if (&src != this)-    {-        VMA_HEAVY_ASSERT(IsEmpty());-        m_Front = src.m_Front;-        m_Back = src.m_Back;-        m_Count = src.m_Count;-        src.m_Front = src.m_Back = VMA_NULL;-        src.m_Count = 0;-    }-    return *this;-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)-{-    VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);-    if (IsEmpty())-    {-        m_Front = item;-        m_Back = item;-        m_Count = 1;-    }-    else-    {-        ItemTypeTraits::AccessPrev(item) = m_Back;-        ItemTypeTraits::AccessNext(m_Back) = item;-        m_Back = item;-        ++m_Count;-    }-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)-{-    VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);-    if (IsEmpty())-    {-        m_Front = item;-        m_Back = item;-        m_Count = 1;-    }-    else-    {-        ItemTypeTraits::AccessNext(item) = m_Front;-        ItemTypeTraits::AccessPrev(m_Front) = item;-        m_Front = item;-        ++m_Count;-    }-}--template<typename ItemTypeTraits>-typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()-{-    VMA_HEAVY_ASSERT(m_Count > 0);-    ItemType* const backItem = m_Back;-    ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);-    if (prevItem != VMA_NULL)-    {-        ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;-    }-    m_Back = prevItem;-    --m_Count;-    ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;-    ItemTypeTraits::AccessNext(backItem) = VMA_NULL;-    return backItem;-}--template<typename ItemTypeTraits>-typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()-{-    VMA_HEAVY_ASSERT(m_Count > 0);-    ItemType* const frontItem = m_Front;-    ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);-    if (nextItem != VMA_NULL)-    {-        ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;-    }-    m_Front = nextItem;-    --m_Count;-    ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;-    ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;-    return frontItem;-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)-{-    VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);-    if (existingItem != VMA_NULL)-    {-        ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);-        ItemTypeTraits::AccessPrev(newItem) = prevItem;-        ItemTypeTraits::AccessNext(newItem) = existingItem;-        ItemTypeTraits::AccessPrev(existingItem) = newItem;-        if (prevItem != VMA_NULL)-        {-            ItemTypeTraits::AccessNext(prevItem) = newItem;-        }-        else-        {-            VMA_HEAVY_ASSERT(m_Front == existingItem);-            m_Front = newItem;-        }-        ++m_Count;-    }-    else-        PushBack(newItem);-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)-{-    VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);-    if (existingItem != VMA_NULL)-    {-        ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);-        ItemTypeTraits::AccessNext(newItem) = nextItem;-        ItemTypeTraits::AccessPrev(newItem) = existingItem;-        ItemTypeTraits::AccessNext(existingItem) = newItem;-        if (nextItem != VMA_NULL)-        {-            ItemTypeTraits::AccessPrev(nextItem) = newItem;-        }-        else-        {-            VMA_HEAVY_ASSERT(m_Back == existingItem);-            m_Back = newItem;-        }-        ++m_Count;-    }-    else-        return PushFront(newItem);-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)-{-    VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);-    if (ItemTypeTraits::GetPrev(item) != VMA_NULL)-    {-        ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);-    }-    else-    {-        VMA_HEAVY_ASSERT(m_Front == item);-        m_Front = ItemTypeTraits::GetNext(item);-    }--    if (ItemTypeTraits::GetNext(item) != VMA_NULL)-    {-        ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);-    }-    else-    {-        VMA_HEAVY_ASSERT(m_Back == item);-        m_Back = ItemTypeTraits::GetPrev(item);-    }-    ItemTypeTraits::AccessPrev(item) = VMA_NULL;-    ItemTypeTraits::AccessNext(item) = VMA_NULL;-    --m_Count;-}--template<typename ItemTypeTraits>-void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()-{-    if (!IsEmpty())-    {-        ItemType* item = m_Back;-        while (item != VMA_NULL)-        {-            ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);-            ItemTypeTraits::AccessPrev(item) = VMA_NULL;-            ItemTypeTraits::AccessNext(item) = VMA_NULL;-            item = prevItem;-        }-        m_Front = VMA_NULL;-        m_Back = VMA_NULL;-        m_Count = 0;-    }-}-#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS-#endif // _VMA_INTRUSIVE_LINKED_LIST--// Unused in this version.-#if 0--#ifndef _VMA_PAIR-template<typename T1, typename T2>-struct VmaPair-{-    T1 first;-    T2 second;--    VmaPair() : first(), second() {}-    VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {}-};--template<typename FirstT, typename SecondT>-struct VmaPairFirstLess-{-    bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const-    {-        return lhs.first < rhs.first;-    }-    bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const-    {-        return lhs.first < rhsFirst;-    }-};-#endif // _VMA_PAIR--#ifndef _VMA_MAP-/* Class compatible with subset of interface of std::unordered_map.-KeyT, ValueT must be POD because they will be stored in VmaVector.-*/-template<typename KeyT, typename ValueT>-class VmaMap-{-public:-    typedef VmaPair<KeyT, ValueT> PairType;-    typedef PairType* iterator;--    VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) {}--    iterator begin() { return m_Vector.begin(); }-    iterator end() { return m_Vector.end(); }-    size_t size() { return m_Vector.size(); }--    void insert(const PairType& pair);-    iterator find(const KeyT& key);-    void erase(iterator it);--private:-    VmaVector< PairType, VmaStlAllocator<PairType>> m_Vector;-};--#ifndef _VMA_MAP_FUNCTIONS-template<typename KeyT, typename ValueT>-void VmaMap<KeyT, ValueT>::insert(const PairType& pair)-{-    const size_t indexToInsert = VmaBinaryFindFirstNotLess(-        m_Vector.data(),-        m_Vector.data() + m_Vector.size(),-        pair,-        VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();-    VmaVectorInsert(m_Vector, indexToInsert, pair);-}--template<typename KeyT, typename ValueT>-VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)-{-    PairType* it = VmaBinaryFindFirstNotLess(-        m_Vector.data(),-        m_Vector.data() + m_Vector.size(),-        key,-        VmaPairFirstLess<KeyT, ValueT>());-    if ((it != m_Vector.end()) && (it->first == key))-    {-        return it;-    }-    else-    {-        return m_Vector.end();-    }-}--template<typename KeyT, typename ValueT>-void VmaMap<KeyT, ValueT>::erase(iterator it)-{-    VmaVectorRemove(m_Vector, it - m_Vector.begin());-}-#endif // _VMA_MAP_FUNCTIONS-#endif // _VMA_MAP--#endif // #if 0--#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED-class VmaStringBuilder-{-public:-    VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}-    ~VmaStringBuilder() = default;--    size_t GetLength() const { return m_Data.size(); }-    const char* GetData() const { return m_Data.data(); }-    void AddNewLine() { Add('\n'); }-    void Add(char ch) { m_Data.push_back(ch); }--    void Add(const char* pStr);-    void AddNumber(uint32_t num);-    void AddNumber(uint64_t num);-    void AddPointer(const void* ptr);--private:-    VmaVector<char, VmaStlAllocator<char>> m_Data;-};--#ifndef _VMA_STRING_BUILDER_FUNCTIONS-void VmaStringBuilder::Add(const char* pStr)-{-    const size_t strLen = strlen(pStr);-    if (strLen > 0)-    {-        const size_t oldCount = m_Data.size();-        m_Data.resize(oldCount + strLen);-        memcpy(m_Data.data() + oldCount, pStr, strLen);-    }-}--void VmaStringBuilder::AddNumber(uint32_t num)-{-    char buf[11];-    buf[10] = '\0';-    char* p = &buf[10];-    do-    {-        *--p = '0' + (char)(num % 10);-        num /= 10;-    } while (num);-    Add(p);-}--void VmaStringBuilder::AddNumber(uint64_t num)-{-    char buf[21];-    buf[20] = '\0';-    char* p = &buf[20];-    do-    {-        *--p = '0' + (char)(num % 10);-        num /= 10;-    } while (num);-    Add(p);-}--void VmaStringBuilder::AddPointer(const void* ptr)-{-    char buf[21];-    VmaPtrToStr(buf, sizeof(buf), ptr);-    Add(buf);-}-#endif //_VMA_STRING_BUILDER_FUNCTIONS-#endif // _VMA_STRING_BUILDER--#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED-/*-Allows to conveniently build a correct JSON document to be written to the-VmaStringBuilder passed to the constructor.-*/-class VmaJsonWriter-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaJsonWriter)-public:-    // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object.-    VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);-    ~VmaJsonWriter();--    // Begins object by writing "{".-    // Inside an object, you must call pairs of WriteString and a value, e.g.:-    // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();-    // Will write: { "A": 1, "B": 2 }-    void BeginObject(bool singleLine = false);-    // Ends object by writing "}".-    void EndObject();--    // Begins array by writing "[".-    // Inside an array, you can write a sequence of any values.-    void BeginArray(bool singleLine = false);-    // Ends array by writing "[".-    void EndArray();--    // Writes a string value inside "".-    // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped.-    void WriteString(const char* pStr);--    // Begins writing a string value.-    // Call BeginString, ContinueString, ContinueString, ..., EndString instead of-    // WriteString to conveniently build the string content incrementally, made of-    // parts including numbers.-    void BeginString(const char* pStr = VMA_NULL);-    // Posts next part of an open string.-    void ContinueString(const char* pStr);-    // Posts next part of an open string. The number is converted to decimal characters.-    void ContinueString(uint32_t n);-    void ContinueString(uint64_t n);-    // Posts next part of an open string. Pointer value is converted to characters-    // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00-    void ContinueString_Pointer(const void* ptr);-    // Ends writing a string value by writing '"'.-    void EndString(const char* pStr = VMA_NULL);--    // Writes a number value.-    void WriteNumber(uint32_t n);-    void WriteNumber(uint64_t n);-    // Writes a boolean value - false or true.-    void WriteBool(bool b);-    // Writes a null value.-    void WriteNull();--private:-    enum COLLECTION_TYPE-    {-        COLLECTION_TYPE_OBJECT,-        COLLECTION_TYPE_ARRAY,-    };-    struct StackItem-    {-        COLLECTION_TYPE type;-        uint32_t valueCount;-        bool singleLineMode;-    };--    static const char* const INDENT;--    VmaStringBuilder& m_SB;-    VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;-    bool m_InsideString;--    void BeginValue(bool isString);-    void WriteIndent(bool oneLess = false);-};-const char* const VmaJsonWriter::INDENT = "  ";--#ifndef _VMA_JSON_WRITER_FUNCTIONS-VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)-    : m_SB(sb),-    m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),-    m_InsideString(false) {}--VmaJsonWriter::~VmaJsonWriter()-{-    VMA_ASSERT(!m_InsideString);-    VMA_ASSERT(m_Stack.empty());-}--void VmaJsonWriter::BeginObject(bool singleLine)-{-    VMA_ASSERT(!m_InsideString);--    BeginValue(false);-    m_SB.Add('{');--    StackItem item;-    item.type = COLLECTION_TYPE_OBJECT;-    item.valueCount = 0;-    item.singleLineMode = singleLine;-    m_Stack.push_back(item);-}--void VmaJsonWriter::EndObject()-{-    VMA_ASSERT(!m_InsideString);--    WriteIndent(true);-    m_SB.Add('}');--    VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);-    m_Stack.pop_back();-}--void VmaJsonWriter::BeginArray(bool singleLine)-{-    VMA_ASSERT(!m_InsideString);--    BeginValue(false);-    m_SB.Add('[');--    StackItem item;-    item.type = COLLECTION_TYPE_ARRAY;-    item.valueCount = 0;-    item.singleLineMode = singleLine;-    m_Stack.push_back(item);-}--void VmaJsonWriter::EndArray()-{-    VMA_ASSERT(!m_InsideString);--    WriteIndent(true);-    m_SB.Add(']');--    VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);-    m_Stack.pop_back();-}--void VmaJsonWriter::WriteString(const char* pStr)-{-    BeginString(pStr);-    EndString();-}--void VmaJsonWriter::BeginString(const char* pStr)-{-    VMA_ASSERT(!m_InsideString);--    BeginValue(true);-    m_SB.Add('"');-    m_InsideString = true;-    if (pStr != VMA_NULL && pStr[0] != '\0')-    {-        ContinueString(pStr);-    }-}--void VmaJsonWriter::ContinueString(const char* pStr)-{-    VMA_ASSERT(m_InsideString);--    const size_t strLen = strlen(pStr);-    for (size_t i = 0; i < strLen; ++i)-    {-        char ch = pStr[i];-        if (ch == '\\')-        {-            m_SB.Add("\\\\");-        }-        else if (ch == '"')-        {-            m_SB.Add("\\\"");-        }-        else if (ch >= 32)-        {-            m_SB.Add(ch);-        }-        else switch (ch)-        {-        case '\b':-            m_SB.Add("\\b");-            break;-        case '\f':-            m_SB.Add("\\f");-            break;-        case '\n':-            m_SB.Add("\\n");-            break;-        case '\r':-            m_SB.Add("\\r");-            break;-        case '\t':-            m_SB.Add("\\t");-            break;-        default:-            VMA_ASSERT(0 && "Character not currently supported.");-        }-    }-}--void VmaJsonWriter::ContinueString(uint32_t n)-{-    VMA_ASSERT(m_InsideString);-    m_SB.AddNumber(n);-}--void VmaJsonWriter::ContinueString(uint64_t n)-{-    VMA_ASSERT(m_InsideString);-    m_SB.AddNumber(n);-}--void VmaJsonWriter::ContinueString_Pointer(const void* ptr)-{-    VMA_ASSERT(m_InsideString);-    m_SB.AddPointer(ptr);-}--void VmaJsonWriter::EndString(const char* pStr)-{-    VMA_ASSERT(m_InsideString);-    if (pStr != VMA_NULL && pStr[0] != '\0')-    {-        ContinueString(pStr);-    }-    m_SB.Add('"');-    m_InsideString = false;-}--void VmaJsonWriter::WriteNumber(uint32_t n)-{-    VMA_ASSERT(!m_InsideString);-    BeginValue(false);-    m_SB.AddNumber(n);-}--void VmaJsonWriter::WriteNumber(uint64_t n)-{-    VMA_ASSERT(!m_InsideString);-    BeginValue(false);-    m_SB.AddNumber(n);-}--void VmaJsonWriter::WriteBool(bool b)-{-    VMA_ASSERT(!m_InsideString);-    BeginValue(false);-    m_SB.Add(b ? "true" : "false");-}--void VmaJsonWriter::WriteNull()-{-    VMA_ASSERT(!m_InsideString);-    BeginValue(false);-    m_SB.Add("null");-}--void VmaJsonWriter::BeginValue(bool isString)-{-    if (!m_Stack.empty())-    {-        StackItem& currItem = m_Stack.back();-        if (currItem.type == COLLECTION_TYPE_OBJECT &&-            currItem.valueCount % 2 == 0)-        {-            VMA_ASSERT(isString);-        }--        if (currItem.type == COLLECTION_TYPE_OBJECT &&-            currItem.valueCount % 2 != 0)-        {-            m_SB.Add(": ");-        }-        else if (currItem.valueCount > 0)-        {-            m_SB.Add(", ");-            WriteIndent();-        }-        else-        {-            WriteIndent();-        }-        ++currItem.valueCount;-    }-}--void VmaJsonWriter::WriteIndent(bool oneLess)-{-    if (!m_Stack.empty() && !m_Stack.back().singleLineMode)-    {-        m_SB.AddNewLine();--        size_t count = m_Stack.size();-        if (count > 0 && oneLess)-        {-            --count;-        }-        for (size_t i = 0; i < count; ++i)-        {-            m_SB.Add(INDENT);-        }-    }-}-#endif // _VMA_JSON_WRITER_FUNCTIONS--static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)-{-    json.BeginObject();--    json.WriteString("BlockCount");-    json.WriteNumber(stat.statistics.blockCount);-    json.WriteString("BlockBytes");-    json.WriteNumber(stat.statistics.blockBytes);-    json.WriteString("AllocationCount");-    json.WriteNumber(stat.statistics.allocationCount);-    json.WriteString("AllocationBytes");-    json.WriteNumber(stat.statistics.allocationBytes);-    json.WriteString("UnusedRangeCount");-    json.WriteNumber(stat.unusedRangeCount);--    if (stat.statistics.allocationCount > 1)-    {-        json.WriteString("AllocationSizeMin");-        json.WriteNumber(stat.allocationSizeMin);-        json.WriteString("AllocationSizeMax");-        json.WriteNumber(stat.allocationSizeMax);-    }-    if (stat.unusedRangeCount > 1)-    {-        json.WriteString("UnusedRangeSizeMin");-        json.WriteNumber(stat.unusedRangeSizeMin);-        json.WriteString("UnusedRangeSizeMax");-        json.WriteNumber(stat.unusedRangeSizeMax);-    }-    json.EndObject();-}-#endif // _VMA_JSON_WRITER--#ifndef _VMA_MAPPING_HYSTERESIS--class VmaMappingHysteresis-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaMappingHysteresis)-public:-    VmaMappingHysteresis() = default;--    uint32_t GetExtraMapping() const { return m_ExtraMapping; }--    // Call when Map was called.-    // Returns true if switched to extra +1 mapping reference count.-    bool PostMap()-    {-#if VMA_MAPPING_HYSTERESIS_ENABLED-        if(m_ExtraMapping == 0)-        {-            ++m_MajorCounter;-            if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)-            {-                m_ExtraMapping = 1;-                m_MajorCounter = 0;-                m_MinorCounter = 0;-                return true;-            }-        }-        else // m_ExtraMapping == 1-            PostMinorCounter();-#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED-        return false;-    }--    // Call when Unmap was called.-    void PostUnmap()-    {-#if VMA_MAPPING_HYSTERESIS_ENABLED-        if(m_ExtraMapping == 0)-            ++m_MajorCounter;-        else // m_ExtraMapping == 1-            PostMinorCounter();-#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED-    }--    // Call when allocation was made from the memory block.-    void PostAlloc()-    {-#if VMA_MAPPING_HYSTERESIS_ENABLED-        if(m_ExtraMapping == 1)-            ++m_MajorCounter;-        else // m_ExtraMapping == 0-            PostMinorCounter();-#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED-    }--    // Call when allocation was freed from the memory block.-    // Returns true if switched to extra -1 mapping reference count.-    bool PostFree()-    {-#if VMA_MAPPING_HYSTERESIS_ENABLED-        if(m_ExtraMapping == 1)-        {-            ++m_MajorCounter;-            if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&-                m_MajorCounter > m_MinorCounter + 1)-            {-                m_ExtraMapping = 0;-                m_MajorCounter = 0;-                m_MinorCounter = 0;-                return true;-            }-        }-        else // m_ExtraMapping == 0-            PostMinorCounter();-#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED-        return false;-    }--private:-    static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;--    uint32_t m_MinorCounter = 0;-    uint32_t m_MajorCounter = 0;-    uint32_t m_ExtraMapping = 0; // 0 or 1.--    void PostMinorCounter()-    {-        if(m_MinorCounter < m_MajorCounter)-        {-            ++m_MinorCounter;-        }-        else if(m_MajorCounter > 0)-        {-            --m_MajorCounter;-            --m_MinorCounter;-        }-    }-};--#endif // _VMA_MAPPING_HYSTERESIS--#ifndef _VMA_DEVICE_MEMORY_BLOCK-/*-Represents a single block of device memory (`VkDeviceMemory`) with all the-data about its regions (aka suballocations, #VmaAllocation), assigned and free.--Thread-safety:-- Access to m_pMetadata must be externally synchronized.-- Map, Unmap, Bind* are synchronized internally.-*/-class VmaDeviceMemoryBlock-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaDeviceMemoryBlock)-public:-    VmaBlockMetadata* m_pMetadata;--    VmaDeviceMemoryBlock(VmaAllocator hAllocator);-    ~VmaDeviceMemoryBlock();--    // Always call after construction.-    void Init(-        VmaAllocator hAllocator,-        VmaPool hParentPool,-        uint32_t newMemoryTypeIndex,-        VkDeviceMemory newMemory,-        VkDeviceSize newSize,-        uint32_t id,-        uint32_t algorithm,-        VkDeviceSize bufferImageGranularity);-    // Always call before destruction.-    void Destroy(VmaAllocator allocator);--    VmaPool GetParentPool() const { return m_hParentPool; }-    VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }-    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }-    uint32_t GetId() const { return m_Id; }-    void* GetMappedData() const { return m_pMappedData; }-    uint32_t GetMapRefCount() const { return m_MapCount; }--    // Call when allocation/free was made from m_pMetadata.-    // Used for m_MappingHysteresis.-    void PostAlloc(VmaAllocator hAllocator);-    void PostFree(VmaAllocator hAllocator);--    // Validates all data structures inside this object. If not valid, returns false.-    bool Validate() const;-    VkResult CheckCorruption(VmaAllocator hAllocator);--    // ppData can be null.-    VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);-    void Unmap(VmaAllocator hAllocator, uint32_t count);--    VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);-    VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);--    VkResult BindBufferMemory(-        const VmaAllocator hAllocator,-        const VmaAllocation hAllocation,-        VkDeviceSize allocationLocalOffset,-        VkBuffer hBuffer,-        const void* pNext);-    VkResult BindImageMemory(-        const VmaAllocator hAllocator,-        const VmaAllocation hAllocation,-        VkDeviceSize allocationLocalOffset,-        VkImage hImage,-        const void* pNext);--private:-    VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.-    uint32_t m_MemoryTypeIndex;-    uint32_t m_Id;-    VkDeviceMemory m_hMemory;--    /*-    Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.-    Also protects m_MapCount, m_pMappedData.-    Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.-    */-    VMA_MUTEX m_MapAndBindMutex;-    VmaMappingHysteresis m_MappingHysteresis;-    uint32_t m_MapCount;-    void* m_pMappedData;-};-#endif // _VMA_DEVICE_MEMORY_BLOCK--#ifndef _VMA_ALLOCATION_T-struct VmaAllocation_T-{-    friend struct VmaDedicatedAllocationListItemTraits;--    enum FLAGS-    {-        FLAG_PERSISTENT_MAP   = 0x01,-        FLAG_MAPPING_ALLOWED  = 0x02,-    };--public:-    enum ALLOCATION_TYPE-    {-        ALLOCATION_TYPE_NONE,-        ALLOCATION_TYPE_BLOCK,-        ALLOCATION_TYPE_DEDICATED,-    };--    // This struct is allocated using VmaPoolAllocator.-    VmaAllocation_T(bool mappingAllowed);-    ~VmaAllocation_T();--    void InitBlockAllocation(-        VmaDeviceMemoryBlock* block,-        VmaAllocHandle allocHandle,-        VkDeviceSize alignment,-        VkDeviceSize size,-        uint32_t memoryTypeIndex,-        VmaSuballocationType suballocationType,-        bool mapped);-    // pMappedData not null means allocation is created with MAPPED flag.-    void InitDedicatedAllocation(-        VmaPool hParentPool,-        uint32_t memoryTypeIndex,-        VkDeviceMemory hMemory,-        VmaSuballocationType suballocationType,-        void* pMappedData,-        VkDeviceSize size);--    ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }-    VkDeviceSize GetAlignment() const { return m_Alignment; }-    VkDeviceSize GetSize() const { return m_Size; }-    void* GetUserData() const { return m_pUserData; }-    const char* GetName() const { return m_pName; }-    VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }--    VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }-    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }-    bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }-    bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }--    void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }-    void SetName(VmaAllocator hAllocator, const char* pName);-    void FreeName(VmaAllocator hAllocator);-    uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);-    VmaAllocHandle GetAllocHandle() const;-    VkDeviceSize GetOffset() const;-    VmaPool GetParentPool() const;-    VkDeviceMemory GetMemory() const;-    void* GetMappedData() const;--    void BlockAllocMap();-    void BlockAllocUnmap();-    VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);-    void DedicatedAllocUnmap(VmaAllocator hAllocator);--#if VMA_STATS_STRING_ENABLED-    uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; }--    void InitBufferImageUsage(uint32_t bufferImageUsage);-    void PrintParameters(class VmaJsonWriter& json) const;-#endif--private:-    // Allocation out of VmaDeviceMemoryBlock.-    struct BlockAllocation-    {-        VmaDeviceMemoryBlock* m_Block;-        VmaAllocHandle m_AllocHandle;-    };-    // Allocation for an object that has its own private VkDeviceMemory.-    struct DedicatedAllocation-    {-        VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.-        VkDeviceMemory m_hMemory;-        void* m_pMappedData; // Not null means memory is mapped.-        VmaAllocation_T* m_Prev;-        VmaAllocation_T* m_Next;-    };-    union-    {-        // Allocation out of VmaDeviceMemoryBlock.-        BlockAllocation m_BlockAllocation;-        // Allocation for an object that has its own private VkDeviceMemory.-        DedicatedAllocation m_DedicatedAllocation;-    };--    VkDeviceSize m_Alignment;-    VkDeviceSize m_Size;-    void* m_pUserData;-    char* m_pName;-    uint32_t m_MemoryTypeIndex;-    uint8_t m_Type; // ALLOCATION_TYPE-    uint8_t m_SuballocationType; // VmaSuballocationType-    // Reference counter for vmaMapMemory()/vmaUnmapMemory().-    uint8_t m_MapCount;-    uint8_t m_Flags; // enum FLAGS-#if VMA_STATS_STRING_ENABLED-    uint32_t m_BufferImageUsage; // 0 if unknown.-#endif-};-#endif // _VMA_ALLOCATION_T--#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS-struct VmaDedicatedAllocationListItemTraits-{-    typedef VmaAllocation_T ItemType;--    static ItemType* GetPrev(const ItemType* item)-    {-        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);-        return item->m_DedicatedAllocation.m_Prev;-    }-    static ItemType* GetNext(const ItemType* item)-    {-        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);-        return item->m_DedicatedAllocation.m_Next;-    }-    static ItemType*& AccessPrev(ItemType* item)-    {-        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);-        return item->m_DedicatedAllocation.m_Prev;-    }-    static ItemType*& AccessNext(ItemType* item)-    {-        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);-        return item->m_DedicatedAllocation.m_Next;-    }-};-#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS--#ifndef _VMA_DEDICATED_ALLOCATION_LIST-/*-Stores linked list of VmaAllocation_T objects.-Thread-safe, synchronized internally.-*/-class VmaDedicatedAllocationList-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaDedicatedAllocationList)-public:-    VmaDedicatedAllocationList() {}-    ~VmaDedicatedAllocationList();--    void Init(bool useMutex) { m_UseMutex = useMutex; }-    bool Validate();--    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);-    void AddStatistics(VmaStatistics& inoutStats);-#if VMA_STATS_STRING_ENABLED-    // Writes JSON array with the list of allocations.-    void BuildStatsString(VmaJsonWriter& json);-#endif--    bool IsEmpty();-    void Register(VmaAllocation alloc);-    void Unregister(VmaAllocation alloc);--private:-    typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;--    bool m_UseMutex = true;-    VMA_RW_MUTEX m_Mutex;-    DedicatedAllocationLinkedList m_AllocationList;-};--#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS--VmaDedicatedAllocationList::~VmaDedicatedAllocationList()-{-    VMA_HEAVY_ASSERT(Validate());--    if (!m_AllocationList.IsEmpty())-    {-        VMA_ASSERT(false && "Unfreed dedicated allocations found!");-    }-}--bool VmaDedicatedAllocationList::Validate()-{-    const size_t declaredCount = m_AllocationList.GetCount();-    size_t actualCount = 0;-    VmaMutexLockRead lock(m_Mutex, m_UseMutex);-    for (VmaAllocation alloc = m_AllocationList.Front();-        alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))-    {-        ++actualCount;-    }-    VMA_VALIDATE(actualCount == declaredCount);--    return true;-}--void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)-{-    for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))-    {-        const VkDeviceSize size = item->GetSize();-        inoutStats.statistics.blockCount++;-        inoutStats.statistics.blockBytes += size;-        VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());-    }-}--void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)-{-    VmaMutexLockRead lock(m_Mutex, m_UseMutex);--    const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();-    inoutStats.blockCount += allocCount;-    inoutStats.allocationCount += allocCount;--    for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))-    {-        const VkDeviceSize size = item->GetSize();-        inoutStats.blockBytes += size;-        inoutStats.allocationBytes += size;-    }-}--#if VMA_STATS_STRING_ENABLED-void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)-{-    VmaMutexLockRead lock(m_Mutex, m_UseMutex);-    json.BeginArray();-    for (VmaAllocation alloc = m_AllocationList.Front();-        alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))-    {-        json.BeginObject(true);-        alloc->PrintParameters(json);-        json.EndObject();-    }-    json.EndArray();-}-#endif // VMA_STATS_STRING_ENABLED--bool VmaDedicatedAllocationList::IsEmpty()-{-    VmaMutexLockRead lock(m_Mutex, m_UseMutex);-    return m_AllocationList.IsEmpty();-}--void VmaDedicatedAllocationList::Register(VmaAllocation alloc)-{-    VmaMutexLockWrite lock(m_Mutex, m_UseMutex);-    m_AllocationList.PushBack(alloc);-}--void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)-{-    VmaMutexLockWrite lock(m_Mutex, m_UseMutex);-    m_AllocationList.Remove(alloc);-}-#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS-#endif // _VMA_DEDICATED_ALLOCATION_LIST--#ifndef _VMA_SUBALLOCATION-/*-Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as-allocated memory block or free.-*/-struct VmaSuballocation-{-    VkDeviceSize offset;-    VkDeviceSize size;-    void* userData;-    VmaSuballocationType type;-};--// Comparator for offsets.-struct VmaSuballocationOffsetLess-{-    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const-    {-        return lhs.offset < rhs.offset;-    }-};--struct VmaSuballocationOffsetGreater-{-    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const-    {-        return lhs.offset > rhs.offset;-    }-};--struct VmaSuballocationItemSizeLess-{-    bool operator()(const VmaSuballocationList::iterator lhs,-        const VmaSuballocationList::iterator rhs) const-    {-        return lhs->size < rhs->size;-    }--    bool operator()(const VmaSuballocationList::iterator lhs,-        VkDeviceSize rhsSize) const-    {-        return lhs->size < rhsSize;-    }-};-#endif // _VMA_SUBALLOCATION--#ifndef _VMA_ALLOCATION_REQUEST-/*-Parameters of planned allocation inside a VmaDeviceMemoryBlock.-item points to a FREE suballocation.-*/-struct VmaAllocationRequest-{-    VmaAllocHandle allocHandle;-    VkDeviceSize size;-    VmaSuballocationList::iterator item;-    void* customData;-    uint64_t algorithmData;-    VmaAllocationRequestType type;-};-#endif // _VMA_ALLOCATION_REQUEST--#ifndef _VMA_BLOCK_METADATA-/*-Data structure used for bookkeeping of allocations and unused ranges of memory-in a single VkDeviceMemory block.-*/-class VmaBlockMetadata-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata)-public:-    // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.-    VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,-        VkDeviceSize bufferImageGranularity, bool isVirtual);-    virtual ~VmaBlockMetadata() = default;--    virtual void Init(VkDeviceSize size) { m_Size = size; }-    bool IsVirtual() const { return m_IsVirtual; }-    VkDeviceSize GetSize() const { return m_Size; }--    // Validates all data structures inside this object. If not valid, returns false.-    virtual bool Validate() const = 0;-    virtual size_t GetAllocationCount() const = 0;-    virtual size_t GetFreeRegionsCount() const = 0;-    virtual VkDeviceSize GetSumFreeSize() const = 0;-    // Returns true if this block is empty - contains only single free suballocation.-    virtual bool IsEmpty() const = 0;-    virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;-    virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;-    virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;--    virtual VmaAllocHandle GetAllocationListBegin() const = 0;-    virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;-    virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;--    // Shouldn't modify blockCount.-    virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;-    virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;--#if VMA_STATS_STRING_ENABLED-    virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;-#endif--    // Tries to find a place for suballocation with given parameters inside this block.-    // If succeeded, fills pAllocationRequest and returns true.-    // If failed, returns false.-    virtual bool CreateAllocationRequest(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        bool upperAddress,-        VmaSuballocationType allocType,-        // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest) = 0;--    virtual VkResult CheckCorruption(const void* pBlockData) = 0;--    // Makes actual allocation based on request. Request must already be checked and valid.-    virtual void Alloc(-        const VmaAllocationRequest& request,-        VmaSuballocationType type,-        void* userData) = 0;--    // Frees suballocation assigned to given memory region.-    virtual void Free(VmaAllocHandle allocHandle) = 0;--    // Frees all allocations.-    // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!-    virtual void Clear() = 0;--    virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;-    virtual void DebugLogAllAllocations() const = 0;--protected:-    const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }-    VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }-    VkDeviceSize GetDebugMargin() const { return VkDeviceSize(IsVirtual() ? 0 : VMA_DEBUG_MARGIN); }--    void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;-#if VMA_STATS_STRING_ENABLED-    // mapRefCount == UINT32_MAX means unspecified.-    void PrintDetailedMap_Begin(class VmaJsonWriter& json,-        VkDeviceSize unusedBytes,-        size_t allocationCount,-        size_t unusedRangeCount) const;-    void PrintDetailedMap_Allocation(class VmaJsonWriter& json,-        VkDeviceSize offset, VkDeviceSize size, void* userData) const;-    void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,-        VkDeviceSize offset,-        VkDeviceSize size) const;-    void PrintDetailedMap_End(class VmaJsonWriter& json) const;-#endif--private:-    VkDeviceSize m_Size;-    const VkAllocationCallbacks* m_pAllocationCallbacks;-    const VkDeviceSize m_BufferImageGranularity;-    const bool m_IsVirtual;-};--#ifndef _VMA_BLOCK_METADATA_FUNCTIONS-VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,-    VkDeviceSize bufferImageGranularity, bool isVirtual)-    : m_Size(0),-    m_pAllocationCallbacks(pAllocationCallbacks),-    m_BufferImageGranularity(bufferImageGranularity),-    m_IsVirtual(isVirtual) {}--void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const-{-    if (IsVirtual())-    {-        VMA_DEBUG_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData);-    }-    else-    {-        VMA_ASSERT(userData != VMA_NULL);-        VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);--        userData = allocation->GetUserData();-        const char* name = allocation->GetName();--#if VMA_STATS_STRING_ENABLED-        VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u",-            offset, size, userData, name ? name : "vma_empty",-            VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],-            allocation->GetBufferImageUsage());-#else-        VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u",-            offset, size, userData, name ? name : "vma_empty",-            (uint32_t)allocation->GetSuballocationType());-#endif // VMA_STATS_STRING_ENABLED-    }--}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,-    VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const-{-    json.WriteString("TotalBytes");-    json.WriteNumber(GetSize());--    json.WriteString("UnusedBytes");-    json.WriteNumber(unusedBytes);--    json.WriteString("Allocations");-    json.WriteNumber((uint64_t)allocationCount);--    json.WriteString("UnusedRanges");-    json.WriteNumber((uint64_t)unusedRangeCount);--    json.WriteString("Suballocations");-    json.BeginArray();-}--void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,-    VkDeviceSize offset, VkDeviceSize size, void* userData) const-{-    json.BeginObject(true);--    json.WriteString("Offset");-    json.WriteNumber(offset);--    if (IsVirtual())-    {-        json.WriteString("Size");-        json.WriteNumber(size);-        if (userData)-        {-            json.WriteString("CustomData");-            json.BeginString();-            json.ContinueString_Pointer(userData);-            json.EndString();-        }-    }-    else-    {-        ((VmaAllocation)userData)->PrintParameters(json);-    }--    json.EndObject();-}--void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,-    VkDeviceSize offset, VkDeviceSize size) const-{-    json.BeginObject(true);--    json.WriteString("Offset");-    json.WriteNumber(offset);--    json.WriteString("Type");-    json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);--    json.WriteString("Size");-    json.WriteNumber(size);--    json.EndObject();-}--void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const-{-    json.EndArray();-}-#endif // VMA_STATS_STRING_ENABLED-#endif // _VMA_BLOCK_METADATA_FUNCTIONS-#endif // _VMA_BLOCK_METADATA--#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY-// Before deleting object of this class remember to call 'Destroy()'-class VmaBlockBufferImageGranularity final-{-public:-    struct ValidationContext-    {-        const VkAllocationCallbacks* allocCallbacks;-        uint16_t* pageAllocs;-    };--    VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);-    ~VmaBlockBufferImageGranularity();--    bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }--    void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);-    // Before destroying object you must call free it's memory-    void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);--    void RoundupAllocRequest(VmaSuballocationType allocType,-        VkDeviceSize& inOutAllocSize,-        VkDeviceSize& inOutAllocAlignment) const;--    bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,-        VkDeviceSize allocSize,-        VkDeviceSize blockOffset,-        VkDeviceSize blockSize,-        VmaSuballocationType allocType) const;--    void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);-    void FreePages(VkDeviceSize offset, VkDeviceSize size);-    void Clear();--    ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,-        bool isVirutal) const;-    bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;-    bool FinishValidation(ValidationContext& ctx) const;--private:-    static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;--    struct RegionInfo-    {-        uint8_t allocType;-        uint16_t allocCount;-    };--    VkDeviceSize m_BufferImageGranularity;-    uint32_t m_RegionCount;-    RegionInfo* m_RegionInfo;--    uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }-    uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }--    uint32_t OffsetToPageIndex(VkDeviceSize offset) const;-    void AllocPage(RegionInfo& page, uint8_t allocType);-};--#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS-VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)-    : m_BufferImageGranularity(bufferImageGranularity),-    m_RegionCount(0),-    m_RegionInfo(VMA_NULL) {}--VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()-{-    VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");-}--void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)-{-    if (IsEnabled())-    {-        m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));-        m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);-        memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));-    }-}--void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)-{-    if (m_RegionInfo)-    {-        vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);-        m_RegionInfo = VMA_NULL;-    }-}--void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,-    VkDeviceSize& inOutAllocSize,-    VkDeviceSize& inOutAllocAlignment) const-{-    if (m_BufferImageGranularity > 1 &&-        m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)-    {-        if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||-            allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||-            allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)-        {-            inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);-            inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);-        }-    }-}--bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,-    VkDeviceSize allocSize,-    VkDeviceSize blockOffset,-    VkDeviceSize blockSize,-    VmaSuballocationType allocType) const-{-    if (IsEnabled())-    {-        uint32_t startPage = GetStartPage(inOutAllocOffset);-        if (m_RegionInfo[startPage].allocCount > 0 &&-            VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))-        {-            inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);-            if (blockSize < allocSize + inOutAllocOffset - blockOffset)-                return true;-            ++startPage;-        }-        uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);-        if (endPage != startPage &&-            m_RegionInfo[endPage].allocCount > 0 &&-            VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))-        {-            return true;-        }-    }-    return false;-}--void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)-{-    if (IsEnabled())-    {-        uint32_t startPage = GetStartPage(offset);-        AllocPage(m_RegionInfo[startPage], allocType);--        uint32_t endPage = GetEndPage(offset, size);-        if (startPage != endPage)-            AllocPage(m_RegionInfo[endPage], allocType);-    }-}--void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)-{-    if (IsEnabled())-    {-        uint32_t startPage = GetStartPage(offset);-        --m_RegionInfo[startPage].allocCount;-        if (m_RegionInfo[startPage].allocCount == 0)-            m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;-        uint32_t endPage = GetEndPage(offset, size);-        if (startPage != endPage)-        {-            --m_RegionInfo[endPage].allocCount;-            if (m_RegionInfo[endPage].allocCount == 0)-                m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;-        }-    }-}--void VmaBlockBufferImageGranularity::Clear()-{-    if (m_RegionInfo)-        memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));-}--VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(-    const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const-{-    ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };-    if (!isVirutal && IsEnabled())-    {-        ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);-        memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));-    }-    return ctx;-}--bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,-    VkDeviceSize offset, VkDeviceSize size) const-{-    if (IsEnabled())-    {-        uint32_t start = GetStartPage(offset);-        ++ctx.pageAllocs[start];-        VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);--        uint32_t end = GetEndPage(offset, size);-        if (start != end)-        {-            ++ctx.pageAllocs[end];-            VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);-        }-    }-    return true;-}--bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const-{-    // Check proper page structure-    if (IsEnabled())-    {-        VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");--        for (uint32_t page = 0; page < m_RegionCount; ++page)-        {-            VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);-        }-        vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);-        ctx.pageAllocs = VMA_NULL;-    }-    return true;-}--uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const-{-    return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));-}--void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)-{-    // When current alloc type is free then it can be overridden by new type-    if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))-        page.allocType = allocType;--    ++page.allocCount;-}-#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS-#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY--#if 0-#ifndef _VMA_BLOCK_METADATA_GENERIC-class VmaBlockMetadata_Generic : public VmaBlockMetadata-{-    friend class VmaDefragmentationAlgorithm_Generic;-    friend class VmaDefragmentationAlgorithm_Fast;-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Generic)-public:-    VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,-        VkDeviceSize bufferImageGranularity, bool isVirtual);-    virtual ~VmaBlockMetadata_Generic() = default;--    size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; }-    VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }-    bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); }-    void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); }-    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }--    void Init(VkDeviceSize size) override;-    bool Validate() const override;--    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;-    void AddStatistics(VmaStatistics& inoutStats) const override;--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;-#endif--    bool CreateAllocationRequest(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        bool upperAddress,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest) override;--    VkResult CheckCorruption(const void* pBlockData) override;--    void Alloc(-        const VmaAllocationRequest& request,-        VmaSuballocationType type,-        void* userData) override;--    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;-    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;-    VmaAllocHandle GetAllocationListBegin() const override;-    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;-    void Clear() override;-    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;-    void DebugLogAllAllocations() const override;--private:-    uint32_t m_FreeCount;-    VkDeviceSize m_SumFreeSize;-    VmaSuballocationList m_Suballocations;-    // Suballocations that are free. Sorted by size, ascending.-    VmaVector<VmaSuballocationList::iterator, VmaStlAllocator<VmaSuballocationList::iterator>> m_FreeSuballocationsBySize;--    VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); }--    VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const;-    bool ValidateFreeSuballocationList() const;--    // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.-    // If yes, fills pOffset and returns true. If no, returns false.-    bool CheckAllocation(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        VmaSuballocationType allocType,-        VmaSuballocationList::const_iterator suballocItem,-        VmaAllocHandle* pAllocHandle) const;--    // Given free suballocation, it merges it with following one, which must also be free.-    void MergeFreeWithNext(VmaSuballocationList::iterator item);-    // Releases given suballocation, making it free.-    // Merges it with adjacent free suballocations if applicable.-    // Returns iterator to new free suballocation at this place.-    VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem);-    // Given free suballocation, it inserts it into sorted list of-    // m_FreeSuballocationsBySize if it is suitable.-    void RegisterFreeSuballocation(VmaSuballocationList::iterator item);-    // Given free suballocation, it removes it from sorted list of-    // m_FreeSuballocationsBySize if it is suitable.-    void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);-};--#ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS-VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks,-    VkDeviceSize bufferImageGranularity, bool isVirtual)-    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),-    m_FreeCount(0),-    m_SumFreeSize(0),-    m_Suballocations(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),-    m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(pAllocationCallbacks)) {}--void VmaBlockMetadata_Generic::Init(VkDeviceSize size)-{-    VmaBlockMetadata::Init(size);--    m_FreeCount = 1;-    m_SumFreeSize = size;--    VmaSuballocation suballoc = {};-    suballoc.offset = 0;-    suballoc.size = size;-    suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;--    m_Suballocations.push_back(suballoc);-    m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());-}--bool VmaBlockMetadata_Generic::Validate() const-{-    VMA_VALIDATE(!m_Suballocations.empty());--    // Expected offset of new suballocation as calculated from previous ones.-    VkDeviceSize calculatedOffset = 0;-    // Expected number of free suballocations as calculated from traversing their list.-    uint32_t calculatedFreeCount = 0;-    // Expected sum size of free suballocations as calculated from traversing their list.-    VkDeviceSize calculatedSumFreeSize = 0;-    // Expected number of free suballocations that should be registered in-    // m_FreeSuballocationsBySize calculated from traversing their list.-    size_t freeSuballocationsToRegister = 0;-    // True if previous visited suballocation was free.-    bool prevFree = false;--    const VkDeviceSize debugMargin = GetDebugMargin();--    for (const auto& subAlloc : m_Suballocations)-    {-        // Actual offset of this suballocation doesn't match expected one.-        VMA_VALIDATE(subAlloc.offset == calculatedOffset);--        const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);-        // Two adjacent free suballocations are invalid. They should be merged.-        VMA_VALIDATE(!prevFree || !currFree);--        VmaAllocation alloc = (VmaAllocation)subAlloc.userData;-        if (!IsVirtual())-        {-            VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));-        }--        if (currFree)-        {-            calculatedSumFreeSize += subAlloc.size;-            ++calculatedFreeCount;-            ++freeSuballocationsToRegister;--            // Margin required between allocations - every free space must be at least that large.-            VMA_VALIDATE(subAlloc.size >= debugMargin);-        }-        else-        {-            if (!IsVirtual())-            {-                VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1);-                VMA_VALIDATE(alloc->GetSize() == subAlloc.size);-            }--            // Margin required between allocations - previous allocation must be free.-            VMA_VALIDATE(debugMargin == 0 || prevFree);-        }--        calculatedOffset += subAlloc.size;-        prevFree = currFree;-    }--    // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't-    // match expected one.-    VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister);--    VkDeviceSize lastSize = 0;-    for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)-    {-        VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];--        // Only free suballocations can be registered in m_FreeSuballocationsBySize.-        VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE);-        // They must be sorted by size ascending.-        VMA_VALIDATE(suballocItem->size >= lastSize);--        lastSize = suballocItem->size;-    }--    // Check if totals match calculated values.-    VMA_VALIDATE(ValidateFreeSuballocationList());-    VMA_VALIDATE(calculatedOffset == GetSize());-    VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize);-    VMA_VALIDATE(calculatedFreeCount == m_FreeCount);--    return true;-}--void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const-{-    const uint32_t rangeCount = (uint32_t)m_Suballocations.size();-    inoutStats.statistics.blockCount++;-    inoutStats.statistics.blockBytes += GetSize();--    for (const auto& suballoc : m_Suballocations)-    {-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-            VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);-        else-            VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size);-    }-}--void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const-{-    inoutStats.blockCount++;-    inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount;-    inoutStats.blockBytes += GetSize();-    inoutStats.allocationBytes += GetSize() - m_SumFreeSize;-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const-{-    PrintDetailedMap_Begin(json,-        m_SumFreeSize, // unusedBytes-        m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount-        m_FreeCount, // unusedRangeCount-        mapRefCount);--    for (const auto& suballoc : m_Suballocations)-    {-        if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size);-        }-        else-        {-            PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);-        }-    }--    PrintDetailedMap_End(json);-}-#endif // VMA_STATS_STRING_ENABLED--bool VmaBlockMetadata_Generic::CreateAllocationRequest(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    bool upperAddress,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    VMA_ASSERT(allocSize > 0);-    VMA_ASSERT(!upperAddress);-    VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);-    VMA_ASSERT(pAllocationRequest != VMA_NULL);-    VMA_HEAVY_ASSERT(Validate());--    allocSize = AlignAllocationSize(allocSize);--    pAllocationRequest->type = VmaAllocationRequestType::Normal;-    pAllocationRequest->size = allocSize;--    const VkDeviceSize debugMargin = GetDebugMargin();--    // There is not enough total free space in this block to fulfill the request: Early return.-    if (m_SumFreeSize < allocSize + debugMargin)-    {-        return false;-    }--    // New algorithm, efficiently searching freeSuballocationsBySize.-    const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();-    if (freeSuballocCount > 0)-    {-        if (strategy == 0 ||-            strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)-        {-            // Find first free suballocation with size not less than allocSize + debugMargin.-            VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(-                m_FreeSuballocationsBySize.data(),-                m_FreeSuballocationsBySize.data() + freeSuballocCount,-                allocSize + debugMargin,-                VmaSuballocationItemSizeLess());-            size_t index = it - m_FreeSuballocationsBySize.data();-            for (; index < freeSuballocCount; ++index)-            {-                if (CheckAllocation(-                    allocSize,-                    allocAlignment,-                    allocType,-                    m_FreeSuballocationsBySize[index],-                    &pAllocationRequest->allocHandle))-                {-                    pAllocationRequest->item = m_FreeSuballocationsBySize[index];-                    return true;-                }-            }-        }-        else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET)-        {-            for (VmaSuballocationList::iterator it = m_Suballocations.begin();-                it != m_Suballocations.end();-                ++it)-            {-                if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation(-                    allocSize,-                    allocAlignment,-                    allocType,-                    it,-                    &pAllocationRequest->allocHandle))-                {-                    pAllocationRequest->item = it;-                    return true;-                }-            }-        }-        else-        {-            VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ));-            // Search staring from biggest suballocations.-            for (size_t index = freeSuballocCount; index--; )-            {-                if (CheckAllocation(-                    allocSize,-                    allocAlignment,-                    allocType,-                    m_FreeSuballocationsBySize[index],-                    &pAllocationRequest->allocHandle))-                {-                    pAllocationRequest->item = m_FreeSuballocationsBySize[index];-                    return true;-                }-            }-        }-    }--    return false;-}--VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData)-{-    for (auto& suballoc : m_Suballocations)-    {-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-        {-            if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))-            {-                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");-                return VK_ERROR_UNKNOWN_COPY;-            }-        }-    }--    return VK_SUCCESS;-}--void VmaBlockMetadata_Generic::Alloc(-    const VmaAllocationRequest& request,-    VmaSuballocationType type,-    void* userData)-{-    VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);-    VMA_ASSERT(request.item != m_Suballocations.end());-    VmaSuballocation& suballoc = *request.item;-    // Given suballocation is a free block.-    VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);--    // Given offset is inside this suballocation.-    VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset);-    const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1;-    VMA_ASSERT(suballoc.size >= paddingBegin + request.size);-    const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size;--    // Unregister this free suballocation from m_FreeSuballocationsBySize and update-    // it to become used.-    UnregisterFreeSuballocation(request.item);--    suballoc.offset = (VkDeviceSize)request.allocHandle - 1;-    suballoc.size = request.size;-    suballoc.type = type;-    suballoc.userData = userData;--    // If there are any free bytes remaining at the end, insert new free suballocation after current one.-    if (paddingEnd)-    {-        VmaSuballocation paddingSuballoc = {};-        paddingSuballoc.offset = suballoc.offset + suballoc.size;-        paddingSuballoc.size = paddingEnd;-        paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;-        VmaSuballocationList::iterator next = request.item;-        ++next;-        const VmaSuballocationList::iterator paddingEndItem =-            m_Suballocations.insert(next, paddingSuballoc);-        RegisterFreeSuballocation(paddingEndItem);-    }--    // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.-    if (paddingBegin)-    {-        VmaSuballocation paddingSuballoc = {};-        paddingSuballoc.offset = suballoc.offset - paddingBegin;-        paddingSuballoc.size = paddingBegin;-        paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;-        const VmaSuballocationList::iterator paddingBeginItem =-            m_Suballocations.insert(request.item, paddingSuballoc);-        RegisterFreeSuballocation(paddingBeginItem);-    }--    // Update totals.-    m_FreeCount = m_FreeCount - 1;-    if (paddingBegin > 0)-    {-        ++m_FreeCount;-    }-    if (paddingEnd > 0)-    {-        ++m_FreeCount;-    }-    m_SumFreeSize -= request.size;-}--void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)-{-    outInfo.offset = (VkDeviceSize)allocHandle - 1;-    const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset);-    outInfo.size = suballoc.size;-    outInfo.pUserData = suballoc.userData;-}--void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const-{-    return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData;-}--VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const-{-    if (IsEmpty())-        return VK_NULL_HANDLE;--    for (const auto& suballoc : m_Suballocations)-    {-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-            return (VmaAllocHandle)(suballoc.offset + 1);-    }-    VMA_ASSERT(false && "Should contain at least 1 allocation!");-    return VK_NULL_HANDLE;-}--VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const-{-    VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1);--    for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it)-    {-        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)-            return (VmaAllocHandle)(it->offset + 1);-    }-    return VK_NULL_HANDLE;-}--void VmaBlockMetadata_Generic::Clear()-{-    const VkDeviceSize size = GetSize();--    VMA_ASSERT(IsVirtual());-    m_FreeCount = 1;-    m_SumFreeSize = size;-    m_Suballocations.clear();-    m_FreeSuballocationsBySize.clear();--    VmaSuballocation suballoc = {};-    suballoc.offset = 0;-    suballoc.size = size;-    suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;-    m_Suballocations.push_back(suballoc);--    m_FreeSuballocationsBySize.push_back(m_Suballocations.begin());-}--void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)-{-    VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1);-    suballoc.userData = userData;-}--void VmaBlockMetadata_Generic::DebugLogAllAllocations() const-{-    for (const auto& suballoc : m_Suballocations)-    {-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-            DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData);-    }-}--VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const-{-    VMA_HEAVY_ASSERT(!m_Suballocations.empty());-    const VkDeviceSize last = m_Suballocations.rbegin()->offset;-    if (last == offset)-        return m_Suballocations.rbegin().drop_const();-    const VkDeviceSize first = m_Suballocations.begin()->offset;-    if (first == offset)-        return m_Suballocations.begin().drop_const();--    const size_t suballocCount = m_Suballocations.size();-    const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount;-    auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator-    {-        for (auto suballocItem = begin;-            suballocItem != end;-            ++suballocItem)-        {-            if (suballocItem->offset == offset)-                return suballocItem.drop_const();-        }-        VMA_ASSERT(false && "Not found!");-        return m_Suballocations.end().drop_const();-    };-    // If requested offset is closer to the end of range, search from the end-    if (offset - first > suballocCount * step / 2)-    {-        return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend());-    }-    return findSuballocation(m_Suballocations.begin(), m_Suballocations.end());-}--bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const-{-    VkDeviceSize lastSize = 0;-    for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i)-    {-        const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i];--        VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE);-        VMA_VALIDATE(it->size >= lastSize);-        lastSize = it->size;-    }-    return true;-}--bool VmaBlockMetadata_Generic::CheckAllocation(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    VmaSuballocationType allocType,-    VmaSuballocationList::const_iterator suballocItem,-    VmaAllocHandle* pAllocHandle) const-{-    VMA_ASSERT(allocSize > 0);-    VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);-    VMA_ASSERT(suballocItem != m_Suballocations.cend());-    VMA_ASSERT(pAllocHandle != VMA_NULL);--    const VkDeviceSize debugMargin = GetDebugMargin();-    const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();--    const VmaSuballocation& suballoc = *suballocItem;-    VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);--    // Size of this suballocation is too small for this request: Early return.-    if (suballoc.size < allocSize)-    {-        return false;-    }--    // Start from offset equal to beginning of this suballocation.-    VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin());--    // Apply debugMargin from the end of previous alloc.-    if (debugMargin > 0)-    {-        offset += debugMargin;-    }--    // Apply alignment.-    offset = VmaAlignUp(offset, allocAlignment);--    // Check previous suballocations for BufferImageGranularity conflicts.-    // Make bigger alignment if necessary.-    if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)-    {-        bool bufferImageGranularityConflict = false;-        VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;-        while (prevSuballocItem != m_Suballocations.cbegin())-        {-            --prevSuballocItem;-            const VmaSuballocation& prevSuballoc = *prevSuballocItem;-            if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity))-            {-                if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))-                {-                    bufferImageGranularityConflict = true;-                    break;-                }-            }-            else-                // Already on previous page.-                break;-        }-        if (bufferImageGranularityConflict)-        {-            offset = VmaAlignUp(offset, bufferImageGranularity);-        }-    }--    // Calculate padding at the beginning based on current offset.-    const VkDeviceSize paddingBegin = offset - suballoc.offset;--    // Fail if requested size plus margin after is bigger than size of this suballocation.-    if (paddingBegin + allocSize + debugMargin > suballoc.size)-    {-        return false;-    }--    // Check next suballocations for BufferImageGranularity conflicts.-    // If conflict exists, allocation cannot be made here.-    if (allocSize % bufferImageGranularity || offset % bufferImageGranularity)-    {-        VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;-        ++nextSuballocItem;-        while (nextSuballocItem != m_Suballocations.cend())-        {-            const VmaSuballocation& nextSuballoc = *nextSuballocItem;-            if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity))-            {-                if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))-                {-                    return false;-                }-            }-            else-            {-                // Already on next page.-                break;-            }-            ++nextSuballocItem;-        }-    }--    *pAllocHandle = (VmaAllocHandle)(offset + 1);-    // All tests passed: Success. pAllocHandle is already filled.-    return true;-}--void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item)-{-    VMA_ASSERT(item != m_Suballocations.end());-    VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);--    VmaSuballocationList::iterator nextItem = item;-    ++nextItem;-    VMA_ASSERT(nextItem != m_Suballocations.end());-    VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);--    item->size += nextItem->size;-    --m_FreeCount;-    m_Suballocations.erase(nextItem);-}--VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem)-{-    // Change this suballocation to be marked as free.-    VmaSuballocation& suballoc = *suballocItem;-    suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;-    suballoc.userData = VMA_NULL;--    // Update totals.-    ++m_FreeCount;-    m_SumFreeSize += suballoc.size;--    // Merge with previous and/or next suballocation if it's also free.-    bool mergeWithNext = false;-    bool mergeWithPrev = false;--    VmaSuballocationList::iterator nextItem = suballocItem;-    ++nextItem;-    if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))-    {-        mergeWithNext = true;-    }--    VmaSuballocationList::iterator prevItem = suballocItem;-    if (suballocItem != m_Suballocations.begin())-    {-        --prevItem;-        if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            mergeWithPrev = true;-        }-    }--    if (mergeWithNext)-    {-        UnregisterFreeSuballocation(nextItem);-        MergeFreeWithNext(suballocItem);-    }--    if (mergeWithPrev)-    {-        UnregisterFreeSuballocation(prevItem);-        MergeFreeWithNext(prevItem);-        RegisterFreeSuballocation(prevItem);-        return prevItem;-    }-    else-    {-        RegisterFreeSuballocation(suballocItem);-        return suballocItem;-    }-}--void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item)-{-    VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);-    VMA_ASSERT(item->size > 0);--    // You may want to enable this validation at the beginning or at the end of-    // this function, depending on what do you want to check.-    VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());--    if (m_FreeSuballocationsBySize.empty())-    {-        m_FreeSuballocationsBySize.push_back(item);-    }-    else-    {-        VmaVectorInsertSorted<VmaSuballocationItemSizeLess>(m_FreeSuballocationsBySize, item);-    }--    //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());-}--void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)-{-    VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);-    VMA_ASSERT(item->size > 0);--    // You may want to enable this validation at the beginning or at the end of-    // this function, depending on what do you want to check.-    VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());--    VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(-        m_FreeSuballocationsBySize.data(),-        m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),-        item,-        VmaSuballocationItemSizeLess());-    for (size_t index = it - m_FreeSuballocationsBySize.data();-        index < m_FreeSuballocationsBySize.size();-        ++index)-    {-        if (m_FreeSuballocationsBySize[index] == item)-        {-            VmaVectorRemove(m_FreeSuballocationsBySize, index);-            return;-        }-        VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");-    }-    VMA_ASSERT(0 && "Not found.");--    //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList());-}-#endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS-#endif // _VMA_BLOCK_METADATA_GENERIC-#endif // #if 0--#ifndef _VMA_BLOCK_METADATA_LINEAR-/*-Allocations and their references in internal data structure look like this:--if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):--        0 +-------+-          |       |-          |       |-          |       |-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount]-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount + 1]-          +-------+-          |  ...  |-          +-------+-          | Alloc |  1st[1st.size() - 1]-          +-------+-          |       |-          |       |-          |       |-GetSize() +-------+--if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):--        0 +-------+-          | Alloc |  2nd[0]-          +-------+-          | Alloc |  2nd[1]-          +-------+-          |  ...  |-          +-------+-          | Alloc |  2nd[2nd.size() - 1]-          +-------+-          |       |-          |       |-          |       |-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount]-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount + 1]-          +-------+-          |  ...  |-          +-------+-          | Alloc |  1st[1st.size() - 1]-          +-------+-          |       |-GetSize() +-------+--if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):--        0 +-------+-          |       |-          |       |-          |       |-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount]-          +-------+-          | Alloc |  1st[m_1stNullItemsBeginCount + 1]-          +-------+-          |  ...  |-          +-------+-          | Alloc |  1st[1st.size() - 1]-          +-------+-          |       |-          |       |-          |       |-          +-------+-          | Alloc |  2nd[2nd.size() - 1]-          +-------+-          |  ...  |-          +-------+-          | Alloc |  2nd[1]-          +-------+-          | Alloc |  2nd[0]-GetSize() +-------+--*/-class VmaBlockMetadata_Linear : public VmaBlockMetadata-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Linear)-public:-    VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,-        VkDeviceSize bufferImageGranularity, bool isVirtual);-    virtual ~VmaBlockMetadata_Linear() = default;--    VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }-    bool IsEmpty() const override { return GetAllocationCount() == 0; }-    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }--    void Init(VkDeviceSize size) override;-    bool Validate() const override;-    size_t GetAllocationCount() const override;-    size_t GetFreeRegionsCount() const override;--    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;-    void AddStatistics(VmaStatistics& inoutStats) const override;--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json) const override;-#endif--    bool CreateAllocationRequest(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        bool upperAddress,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest) override;--    VkResult CheckCorruption(const void* pBlockData) override;--    void Alloc(-        const VmaAllocationRequest& request,-        VmaSuballocationType type,-        void* userData) override;--    void Free(VmaAllocHandle allocHandle) override;-    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;-    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;-    VmaAllocHandle GetAllocationListBegin() const override;-    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;-    VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;-    void Clear() override;-    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;-    void DebugLogAllAllocations() const override;--private:-    /*-    There are two suballocation vectors, used in ping-pong way.-    The one with index m_1stVectorIndex is called 1st.-    The one with index (m_1stVectorIndex ^ 1) is called 2nd.-    2nd can be non-empty only when 1st is not empty.-    When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.-    */-    typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;--    enum SECOND_VECTOR_MODE-    {-        SECOND_VECTOR_EMPTY,-        /*-        Suballocations in 2nd vector are created later than the ones in 1st, but they-        all have smaller offset.-        */-        SECOND_VECTOR_RING_BUFFER,-        /*-        Suballocations in 2nd vector are upper side of double stack.-        They all have offsets higher than those in 1st vector.-        Top of this stack means smaller offsets, but higher indices in this vector.-        */-        SECOND_VECTOR_DOUBLE_STACK,-    };--    VkDeviceSize m_SumFreeSize;-    SuballocationVectorType m_Suballocations0, m_Suballocations1;-    uint32_t m_1stVectorIndex;-    SECOND_VECTOR_MODE m_2ndVectorMode;-    // Number of items in 1st vector with hAllocation = null at the beginning.-    size_t m_1stNullItemsBeginCount;-    // Number of other items in 1st vector with hAllocation = null somewhere in the middle.-    size_t m_1stNullItemsMiddleCount;-    // Number of items in 2nd vector with hAllocation = null.-    size_t m_2ndNullItemsCount;--    SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }-    SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }-    const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }-    const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }--    VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;-    bool ShouldCompact1st() const;-    void CleanupAfterFree();--    bool CreateAllocationRequest_LowerAddress(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest);-    bool CreateAllocationRequest_UpperAddress(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest);-};--#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS-VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,-    VkDeviceSize bufferImageGranularity, bool isVirtual)-    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),-    m_SumFreeSize(0),-    m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),-    m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),-    m_1stVectorIndex(0),-    m_2ndVectorMode(SECOND_VECTOR_EMPTY),-    m_1stNullItemsBeginCount(0),-    m_1stNullItemsMiddleCount(0),-    m_2ndNullItemsCount(0) {}--void VmaBlockMetadata_Linear::Init(VkDeviceSize size)-{-    VmaBlockMetadata::Init(size);-    m_SumFreeSize = size;-}--bool VmaBlockMetadata_Linear::Validate() const-{-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--    VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));-    VMA_VALIDATE(!suballocations1st.empty() ||-        suballocations2nd.empty() ||-        m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);--    if (!suballocations1st.empty())-    {-        // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.-        VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);-        // Null item at the end should be just pop_back().-        VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);-    }-    if (!suballocations2nd.empty())-    {-        // Null item at the end should be just pop_back().-        VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);-    }--    VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());-    VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());--    VkDeviceSize sumUsedSize = 0;-    const size_t suballoc1stCount = suballocations1st.size();-    const VkDeviceSize debugMargin = GetDebugMargin();-    VkDeviceSize offset = 0;--    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        const size_t suballoc2ndCount = suballocations2nd.size();-        size_t nullItem2ndCount = 0;-        for (size_t i = 0; i < suballoc2ndCount; ++i)-        {-            const VmaSuballocation& suballoc = suballocations2nd[i];-            const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);--            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;-            if (!IsVirtual())-            {-                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));-            }-            VMA_VALIDATE(suballoc.offset >= offset);--            if (!currFree)-            {-                if (!IsVirtual())-                {-                    VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);-                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);-                }-                sumUsedSize += suballoc.size;-            }-            else-            {-                ++nullItem2ndCount;-            }--            offset = suballoc.offset + suballoc.size + debugMargin;-        }--        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);-    }--    for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)-    {-        const VmaSuballocation& suballoc = suballocations1st[i];-        VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&-            suballoc.userData == VMA_NULL);-    }--    size_t nullItem1stCount = m_1stNullItemsBeginCount;--    for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)-    {-        const VmaSuballocation& suballoc = suballocations1st[i];-        const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);--        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;-        if (!IsVirtual())-        {-            VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));-        }-        VMA_VALIDATE(suballoc.offset >= offset);-        VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);--        if (!currFree)-        {-            if (!IsVirtual())-            {-                VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);-                VMA_VALIDATE(alloc->GetSize() == suballoc.size);-            }-            sumUsedSize += suballoc.size;-        }-        else-        {-            ++nullItem1stCount;-        }--        offset = suballoc.offset + suballoc.size + debugMargin;-    }-    VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);--    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        const size_t suballoc2ndCount = suballocations2nd.size();-        size_t nullItem2ndCount = 0;-        for (size_t i = suballoc2ndCount; i--; )-        {-            const VmaSuballocation& suballoc = suballocations2nd[i];-            const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);--            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;-            if (!IsVirtual())-            {-                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));-            }-            VMA_VALIDATE(suballoc.offset >= offset);--            if (!currFree)-            {-                if (!IsVirtual())-                {-                    VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);-                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);-                }-                sumUsedSize += suballoc.size;-            }-            else-            {-                ++nullItem2ndCount;-            }--            offset = suballoc.offset + suballoc.size + debugMargin;-        }--        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);-    }--    VMA_VALIDATE(offset <= GetSize());-    VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);--    return true;-}--size_t VmaBlockMetadata_Linear::GetAllocationCount() const-{-    return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount +-        AccessSuballocations2nd().size() - m_2ndNullItemsCount;-}--size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    VMA_ASSERT(0);-    return SIZE_MAX;-}--void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const-{-    const VkDeviceSize size = GetSize();-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    const size_t suballoc1stCount = suballocations1st.size();-    const size_t suballoc2ndCount = suballocations2nd.size();--    inoutStats.statistics.blockCount++;-    inoutStats.statistics.blockBytes += size;--    VkDeviceSize lastOffset = 0;--    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;-        size_t nextAlloc2ndIndex = 0;-        while (lastOffset < freeSpace2ndTo1stEnd)-        {-            // Find next non-null allocation or move nextAllocIndex to the end.-            while (nextAlloc2ndIndex < suballoc2ndCount &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                ++nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex < suballoc2ndCount)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                ++nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                // There is free space from lastOffset to freeSpace2ndTo1stEnd.-                if (lastOffset < freeSpace2ndTo1stEnd)-                {-                    const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;-                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-                }--                // End of loop.-                lastOffset = freeSpace2ndTo1stEnd;-            }-        }-    }--    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;-    const VkDeviceSize freeSpace1stTo2ndEnd =-        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;-    while (lastOffset < freeSpace1stTo2ndEnd)-    {-        // Find next non-null allocation or move nextAllocIndex to the end.-        while (nextAlloc1stIndex < suballoc1stCount &&-            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)-        {-            ++nextAlloc1stIndex;-        }--        // Found non-null allocation.-        if (nextAlloc1stIndex < suballoc1stCount)-        {-            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];--            // 1. Process free space before this allocation.-            if (lastOffset < suballoc.offset)-            {-                // There is free space from lastOffset to suballoc.offset.-                const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-            }--            // 2. Process this allocation.-            // There is allocation with suballoc.offset, suballoc.size.-            VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);--            // 3. Prepare for next iteration.-            lastOffset = suballoc.offset + suballoc.size;-            ++nextAlloc1stIndex;-        }-        // We are at the end.-        else-        {-            // There is free space from lastOffset to freeSpace1stTo2ndEnd.-            if (lastOffset < freeSpace1stTo2ndEnd)-            {-                const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;-                VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-            }--            // End of loop.-            lastOffset = freeSpace1stTo2ndEnd;-        }-    }--    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;-        while (lastOffset < size)-        {-            // Find next non-null allocation or move nextAllocIndex to the end.-            while (nextAlloc2ndIndex != SIZE_MAX &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                --nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex != SIZE_MAX)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                --nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                // There is free space from lastOffset to size.-                if (lastOffset < size)-                {-                    const VkDeviceSize unusedRangeSize = size - lastOffset;-                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);-                }--                // End of loop.-                lastOffset = size;-            }-        }-    }-}--void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const-{-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    const VkDeviceSize size = GetSize();-    const size_t suballoc1stCount = suballocations1st.size();-    const size_t suballoc2ndCount = suballocations2nd.size();--    inoutStats.blockCount++;-    inoutStats.blockBytes += size;-    inoutStats.allocationBytes += size - m_SumFreeSize;--    VkDeviceSize lastOffset = 0;--    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;-        size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;-        while (lastOffset < freeSpace2ndTo1stEnd)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex < suballoc2ndCount &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                ++nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex < suballoc2ndCount)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                ++inoutStats.allocationCount;--                // Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                ++nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                // End of loop.-                lastOffset = freeSpace2ndTo1stEnd;-            }-        }-    }--    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;-    const VkDeviceSize freeSpace1stTo2ndEnd =-        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;-    while (lastOffset < freeSpace1stTo2ndEnd)-    {-        // Find next non-null allocation or move nextAllocIndex to the end.-        while (nextAlloc1stIndex < suballoc1stCount &&-            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)-        {-            ++nextAlloc1stIndex;-        }--        // Found non-null allocation.-        if (nextAlloc1stIndex < suballoc1stCount)-        {-            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];--            // Process this allocation.-            // There is allocation with suballoc.offset, suballoc.size.-            ++inoutStats.allocationCount;--            // Prepare for next iteration.-            lastOffset = suballoc.offset + suballoc.size;-            ++nextAlloc1stIndex;-        }-        // We are at the end.-        else-        {-            // End of loop.-            lastOffset = freeSpace1stTo2ndEnd;-        }-    }--    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;-        while (lastOffset < size)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex != SIZE_MAX &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                --nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex != SIZE_MAX)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                ++inoutStats.allocationCount;--                // Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                --nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                // End of loop.-                lastOffset = size;-            }-        }-    }-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const-{-    const VkDeviceSize size = GetSize();-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    const size_t suballoc1stCount = suballocations1st.size();-    const size_t suballoc2ndCount = suballocations2nd.size();--    // FIRST PASS--    size_t unusedRangeCount = 0;-    VkDeviceSize usedBytes = 0;--    VkDeviceSize lastOffset = 0;--    size_t alloc2ndCount = 0;-    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;-        size_t nextAlloc2ndIndex = 0;-        while (lastOffset < freeSpace2ndTo1stEnd)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex < suballoc2ndCount &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                ++nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex < suballoc2ndCount)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    ++unusedRangeCount;-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                ++alloc2ndCount;-                usedBytes += suballoc.size;--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                ++nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                if (lastOffset < freeSpace2ndTo1stEnd)-                {-                    // There is free space from lastOffset to freeSpace2ndTo1stEnd.-                    ++unusedRangeCount;-                }--                // End of loop.-                lastOffset = freeSpace2ndTo1stEnd;-            }-        }-    }--    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;-    size_t alloc1stCount = 0;-    const VkDeviceSize freeSpace1stTo2ndEnd =-        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;-    while (lastOffset < freeSpace1stTo2ndEnd)-    {-        // Find next non-null allocation or move nextAllocIndex to the end.-        while (nextAlloc1stIndex < suballoc1stCount &&-            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)-        {-            ++nextAlloc1stIndex;-        }--        // Found non-null allocation.-        if (nextAlloc1stIndex < suballoc1stCount)-        {-            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];--            // 1. Process free space before this allocation.-            if (lastOffset < suballoc.offset)-            {-                // There is free space from lastOffset to suballoc.offset.-                ++unusedRangeCount;-            }--            // 2. Process this allocation.-            // There is allocation with suballoc.offset, suballoc.size.-            ++alloc1stCount;-            usedBytes += suballoc.size;--            // 3. Prepare for next iteration.-            lastOffset = suballoc.offset + suballoc.size;-            ++nextAlloc1stIndex;-        }-        // We are at the end.-        else-        {-            if (lastOffset < size)-            {-                // There is free space from lastOffset to freeSpace1stTo2ndEnd.-                ++unusedRangeCount;-            }--            // End of loop.-            lastOffset = freeSpace1stTo2ndEnd;-        }-    }--    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;-        while (lastOffset < size)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex != SIZE_MAX &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                --nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex != SIZE_MAX)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    ++unusedRangeCount;-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                ++alloc2ndCount;-                usedBytes += suballoc.size;--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                --nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                if (lastOffset < size)-                {-                    // There is free space from lastOffset to size.-                    ++unusedRangeCount;-                }--                // End of loop.-                lastOffset = size;-            }-        }-    }--    const VkDeviceSize unusedBytes = size - usedBytes;-    PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);--    // SECOND PASS-    lastOffset = 0;--    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;-        size_t nextAlloc2ndIndex = 0;-        while (lastOffset < freeSpace2ndTo1stEnd)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex < suballoc2ndCount &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                ++nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex < suballoc2ndCount)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                ++nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                if (lastOffset < freeSpace2ndTo1stEnd)-                {-                    // There is free space from lastOffset to freeSpace2ndTo1stEnd.-                    const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;-                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-                }--                // End of loop.-                lastOffset = freeSpace2ndTo1stEnd;-            }-        }-    }--    nextAlloc1stIndex = m_1stNullItemsBeginCount;-    while (lastOffset < freeSpace1stTo2ndEnd)-    {-        // Find next non-null allocation or move nextAllocIndex to the end.-        while (nextAlloc1stIndex < suballoc1stCount &&-            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)-        {-            ++nextAlloc1stIndex;-        }--        // Found non-null allocation.-        if (nextAlloc1stIndex < suballoc1stCount)-        {-            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];--            // 1. Process free space before this allocation.-            if (lastOffset < suballoc.offset)-            {-                // There is free space from lastOffset to suballoc.offset.-                const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-            }--            // 2. Process this allocation.-            // There is allocation with suballoc.offset, suballoc.size.-            PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);--            // 3. Prepare for next iteration.-            lastOffset = suballoc.offset + suballoc.size;-            ++nextAlloc1stIndex;-        }-        // We are at the end.-        else-        {-            if (lastOffset < freeSpace1stTo2ndEnd)-            {-                // There is free space from lastOffset to freeSpace1stTo2ndEnd.-                const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;-                PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-            }--            // End of loop.-            lastOffset = freeSpace1stTo2ndEnd;-        }-    }--    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;-        while (lastOffset < size)-        {-            // Find next non-null allocation or move nextAlloc2ndIndex to the end.-            while (nextAlloc2ndIndex != SIZE_MAX &&-                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)-            {-                --nextAlloc2ndIndex;-            }--            // Found non-null allocation.-            if (nextAlloc2ndIndex != SIZE_MAX)-            {-                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];--                // 1. Process free space before this allocation.-                if (lastOffset < suballoc.offset)-                {-                    // There is free space from lastOffset to suballoc.offset.-                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;-                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-                }--                // 2. Process this allocation.-                // There is allocation with suballoc.offset, suballoc.size.-                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);--                // 3. Prepare for next iteration.-                lastOffset = suballoc.offset + suballoc.size;-                --nextAlloc2ndIndex;-            }-            // We are at the end.-            else-            {-                if (lastOffset < size)-                {-                    // There is free space from lastOffset to size.-                    const VkDeviceSize unusedRangeSize = size - lastOffset;-                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);-                }--                // End of loop.-                lastOffset = size;-            }-        }-    }--    PrintDetailedMap_End(json);-}-#endif // VMA_STATS_STRING_ENABLED--bool VmaBlockMetadata_Linear::CreateAllocationRequest(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    bool upperAddress,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    VMA_ASSERT(allocSize > 0);-    VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);-    VMA_ASSERT(pAllocationRequest != VMA_NULL);-    VMA_HEAVY_ASSERT(Validate());-    pAllocationRequest->size = allocSize;-    return upperAddress ?-        CreateAllocationRequest_UpperAddress(-            allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :-        CreateAllocationRequest_LowerAddress(-            allocSize, allocAlignment, allocType, strategy, pAllocationRequest);-}--VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)-{-    VMA_ASSERT(!IsVirtual());-    SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)-    {-        const VmaSuballocation& suballoc = suballocations1st[i];-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-        {-            if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))-            {-                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");-                return VK_ERROR_UNKNOWN_COPY;-            }-        }-    }--    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)-    {-        const VmaSuballocation& suballoc = suballocations2nd[i];-        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)-        {-            if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))-            {-                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");-                return VK_ERROR_UNKNOWN_COPY;-            }-        }-    }--    return VK_SUCCESS;-}--void VmaBlockMetadata_Linear::Alloc(-    const VmaAllocationRequest& request,-    VmaSuballocationType type,-    void* userData)-{-    const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;-    const VmaSuballocation newSuballoc = { offset, request.size, userData, type };--    switch (request.type)-    {-    case VmaAllocationRequestType::UpperAddress:-    {-        VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&-            "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");-        SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-        suballocations2nd.push_back(newSuballoc);-        m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;-    }-    break;-    case VmaAllocationRequestType::EndOf1st:-    {-        SuballocationVectorType& suballocations1st = AccessSuballocations1st();--        VMA_ASSERT(suballocations1st.empty() ||-            offset >= suballocations1st.back().offset + suballocations1st.back().size);-        // Check if it fits before the end of the block.-        VMA_ASSERT(offset + request.size <= GetSize());--        suballocations1st.push_back(newSuballoc);-    }-    break;-    case VmaAllocationRequestType::EndOf2nd:-    {-        SuballocationVectorType& suballocations1st = AccessSuballocations1st();-        // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.-        VMA_ASSERT(!suballocations1st.empty() &&-            offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);-        SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--        switch (m_2ndVectorMode)-        {-        case SECOND_VECTOR_EMPTY:-            // First allocation from second part ring buffer.-            VMA_ASSERT(suballocations2nd.empty());-            m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;-            break;-        case SECOND_VECTOR_RING_BUFFER:-            // 2-part ring buffer is already started.-            VMA_ASSERT(!suballocations2nd.empty());-            break;-        case SECOND_VECTOR_DOUBLE_STACK:-            VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");-            break;-        default:-            VMA_ASSERT(0);-        }--        suballocations2nd.push_back(newSuballoc);-    }-    break;-    default:-        VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");-    }--    m_SumFreeSize -= newSuballoc.size;-}--void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)-{-    SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;--    if (!suballocations1st.empty())-    {-        // First allocation: Mark it as next empty at the beginning.-        VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];-        if (firstSuballoc.offset == offset)-        {-            firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;-            firstSuballoc.userData = VMA_NULL;-            m_SumFreeSize += firstSuballoc.size;-            ++m_1stNullItemsBeginCount;-            CleanupAfterFree();-            return;-        }-    }--    // Last allocation in 2-part ring buffer or top of upper stack (same logic).-    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||-        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        VmaSuballocation& lastSuballoc = suballocations2nd.back();-        if (lastSuballoc.offset == offset)-        {-            m_SumFreeSize += lastSuballoc.size;-            suballocations2nd.pop_back();-            CleanupAfterFree();-            return;-        }-    }-    // Last allocation in 1st vector.-    else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)-    {-        VmaSuballocation& lastSuballoc = suballocations1st.back();-        if (lastSuballoc.offset == offset)-        {-            m_SumFreeSize += lastSuballoc.size;-            suballocations1st.pop_back();-            CleanupAfterFree();-            return;-        }-    }--    VmaSuballocation refSuballoc;-    refSuballoc.offset = offset;-    // Rest of members stays uninitialized intentionally for better performance.--    // Item from the middle of 1st vector.-    {-        const SuballocationVectorType::iterator it = VmaBinaryFindSorted(-            suballocations1st.begin() + m_1stNullItemsBeginCount,-            suballocations1st.end(),-            refSuballoc,-            VmaSuballocationOffsetLess());-        if (it != suballocations1st.end())-        {-            it->type = VMA_SUBALLOCATION_TYPE_FREE;-            it->userData = VMA_NULL;-            ++m_1stNullItemsMiddleCount;-            m_SumFreeSize += it->size;-            CleanupAfterFree();-            return;-        }-    }--    if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)-    {-        // Item from the middle of 2nd vector.-        const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?-            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :-            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());-        if (it != suballocations2nd.end())-        {-            it->type = VMA_SUBALLOCATION_TYPE_FREE;-            it->userData = VMA_NULL;-            ++m_2ndNullItemsCount;-            m_SumFreeSize += it->size;-            CleanupAfterFree();-            return;-        }-    }--    VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");-}--void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)-{-    outInfo.offset = (VkDeviceSize)allocHandle - 1;-    VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);-    outInfo.size = suballoc.size;-    outInfo.pUserData = suballoc.userData;-}--void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const-{-    return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;-}--VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    VMA_ASSERT(0);-    return VK_NULL_HANDLE;-}--VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    VMA_ASSERT(0);-    return VK_NULL_HANDLE;-}--VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    VMA_ASSERT(0);-    return 0;-}--void VmaBlockMetadata_Linear::Clear()-{-    m_SumFreeSize = GetSize();-    m_Suballocations0.clear();-    m_Suballocations1.clear();-    // Leaving m_1stVectorIndex unchanged - it doesn't matter.-    m_2ndVectorMode = SECOND_VECTOR_EMPTY;-    m_1stNullItemsBeginCount = 0;-    m_1stNullItemsMiddleCount = 0;-    m_2ndNullItemsCount = 0;-}--void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)-{-    VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);-    suballoc.userData = userData;-}--void VmaBlockMetadata_Linear::DebugLogAllAllocations() const-{-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)-        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)-            DebugLogAllocation(it->offset, it->size, it->userData);--    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();-    for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)-        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)-            DebugLogAllocation(it->offset, it->size, it->userData);-}--VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const-{-    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--    VmaSuballocation refSuballoc;-    refSuballoc.offset = offset;-    // Rest of members stays uninitialized intentionally for better performance.--    // Item from the 1st vector.-    {-        SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(-            suballocations1st.begin() + m_1stNullItemsBeginCount,-            suballocations1st.end(),-            refSuballoc,-            VmaSuballocationOffsetLess());-        if (it != suballocations1st.end())-        {-            return const_cast<VmaSuballocation&>(*it);-        }-    }--    if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)-    {-        // Rest of members stays uninitialized intentionally for better performance.-        SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?-            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :-            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());-        if (it != suballocations2nd.end())-        {-            return const_cast<VmaSuballocation&>(*it);-        }-    }--    VMA_ASSERT(0 && "Allocation not found in linear allocator!");-    return const_cast<VmaSuballocation&>(suballocations1st.back()); // Should never occur.-}--bool VmaBlockMetadata_Linear::ShouldCompact1st() const-{-    const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;-    const size_t suballocCount = AccessSuballocations1st().size();-    return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;-}--void VmaBlockMetadata_Linear::CleanupAfterFree()-{-    SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--    if (IsEmpty())-    {-        suballocations1st.clear();-        suballocations2nd.clear();-        m_1stNullItemsBeginCount = 0;-        m_1stNullItemsMiddleCount = 0;-        m_2ndNullItemsCount = 0;-        m_2ndVectorMode = SECOND_VECTOR_EMPTY;-    }-    else-    {-        const size_t suballoc1stCount = suballocations1st.size();-        const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;-        VMA_ASSERT(nullItem1stCount <= suballoc1stCount);--        // Find more null items at the beginning of 1st vector.-        while (m_1stNullItemsBeginCount < suballoc1stCount &&-            suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            ++m_1stNullItemsBeginCount;-            --m_1stNullItemsMiddleCount;-        }--        // Find more null items at the end of 1st vector.-        while (m_1stNullItemsMiddleCount > 0 &&-            suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            --m_1stNullItemsMiddleCount;-            suballocations1st.pop_back();-        }--        // Find more null items at the end of 2nd vector.-        while (m_2ndNullItemsCount > 0 &&-            suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            --m_2ndNullItemsCount;-            suballocations2nd.pop_back();-        }--        // Find more null items at the beginning of 2nd vector.-        while (m_2ndNullItemsCount > 0 &&-            suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)-        {-            --m_2ndNullItemsCount;-            VmaVectorRemove(suballocations2nd, 0);-        }--        if (ShouldCompact1st())-        {-            const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;-            size_t srcIndex = m_1stNullItemsBeginCount;-            for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)-            {-                while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)-                {-                    ++srcIndex;-                }-                if (dstIndex != srcIndex)-                {-                    suballocations1st[dstIndex] = suballocations1st[srcIndex];-                }-                ++srcIndex;-            }-            suballocations1st.resize(nonNullItemCount);-            m_1stNullItemsBeginCount = 0;-            m_1stNullItemsMiddleCount = 0;-        }--        // 2nd vector became empty.-        if (suballocations2nd.empty())-        {-            m_2ndVectorMode = SECOND_VECTOR_EMPTY;-        }--        // 1st vector became empty.-        if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)-        {-            suballocations1st.clear();-            m_1stNullItemsBeginCount = 0;--            if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-            {-                // Swap 1st with 2nd. Now 2nd is empty.-                m_2ndVectorMode = SECOND_VECTOR_EMPTY;-                m_1stNullItemsMiddleCount = m_2ndNullItemsCount;-                while (m_1stNullItemsBeginCount < suballocations2nd.size() &&-                    suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)-                {-                    ++m_1stNullItemsBeginCount;-                    --m_1stNullItemsMiddleCount;-                }-                m_2ndNullItemsCount = 0;-                m_1stVectorIndex ^= 1;-            }-        }-    }--    VMA_HEAVY_ASSERT(Validate());-}--bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    const VkDeviceSize blockSize = GetSize();-    const VkDeviceSize debugMargin = GetDebugMargin();-    const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();-    SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--    if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-    {-        // Try to allocate at the end of 1st vector.--        VkDeviceSize resultBaseOffset = 0;-        if (!suballocations1st.empty())-        {-            const VmaSuballocation& lastSuballoc = suballocations1st.back();-            resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;-        }--        // Start from offset equal to beginning of free space.-        VkDeviceSize resultOffset = resultBaseOffset;--        // Apply alignment.-        resultOffset = VmaAlignUp(resultOffset, allocAlignment);--        // Check previous suballocations for BufferImageGranularity conflicts.-        // Make bigger alignment if necessary.-        if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())-        {-            bool bufferImageGranularityConflict = false;-            for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )-            {-                const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];-                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))-                {-                    if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))-                    {-                        bufferImageGranularityConflict = true;-                        break;-                    }-                }-                else-                    // Already on previous page.-                    break;-            }-            if (bufferImageGranularityConflict)-            {-                resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);-            }-        }--        const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?-            suballocations2nd.back().offset : blockSize;--        // There is enough free space at the end after alignment.-        if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)-        {-            // Check next suballocations for BufferImageGranularity conflicts.-            // If conflict exists, allocation cannot be made here.-            if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)-            {-                for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )-                {-                    const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];-                    if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))-                    {-                        if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))-                        {-                            return false;-                        }-                    }-                    else-                    {-                        // Already on previous page.-                        break;-                    }-                }-            }--            // All tests passed: Success.-            pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);-            // pAllocationRequest->item, customData unused.-            pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;-            return true;-        }-    }--    // Wrap-around to end of 2nd vector. Try to allocate there, watching for the-    // beginning of 1st vector as the end of free space.-    if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        VMA_ASSERT(!suballocations1st.empty());--        VkDeviceSize resultBaseOffset = 0;-        if (!suballocations2nd.empty())-        {-            const VmaSuballocation& lastSuballoc = suballocations2nd.back();-            resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;-        }--        // Start from offset equal to beginning of free space.-        VkDeviceSize resultOffset = resultBaseOffset;--        // Apply alignment.-        resultOffset = VmaAlignUp(resultOffset, allocAlignment);--        // Check previous suballocations for BufferImageGranularity conflicts.-        // Make bigger alignment if necessary.-        if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())-        {-            bool bufferImageGranularityConflict = false;-            for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )-            {-                const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];-                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))-                {-                    if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))-                    {-                        bufferImageGranularityConflict = true;-                        break;-                    }-                }-                else-                    // Already on previous page.-                    break;-            }-            if (bufferImageGranularityConflict)-            {-                resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);-            }-        }--        size_t index1st = m_1stNullItemsBeginCount;--        // There is enough free space at the end after alignment.-        if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||-            (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))-        {-            // Check next suballocations for BufferImageGranularity conflicts.-            // If conflict exists, allocation cannot be made here.-            if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)-            {-                for (size_t nextSuballocIndex = index1st;-                    nextSuballocIndex < suballocations1st.size();-                    nextSuballocIndex++)-                {-                    const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];-                    if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))-                    {-                        if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))-                        {-                            return false;-                        }-                    }-                    else-                    {-                        // Already on next page.-                        break;-                    }-                }-            }--            // All tests passed: Success.-            pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);-            pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;-            // pAllocationRequest->item, customData unused.-            return true;-        }-    }--    return false;-}--bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    const VkDeviceSize blockSize = GetSize();-    const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();-    SuballocationVectorType& suballocations1st = AccessSuballocations1st();-    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();--    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)-    {-        VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");-        return false;-    }--    // Try to allocate before 2nd.back(), or end of block if 2nd.empty().-    if (allocSize > blockSize)-    {-        return false;-    }-    VkDeviceSize resultBaseOffset = blockSize - allocSize;-    if (!suballocations2nd.empty())-    {-        const VmaSuballocation& lastSuballoc = suballocations2nd.back();-        resultBaseOffset = lastSuballoc.offset - allocSize;-        if (allocSize > lastSuballoc.offset)-        {-            return false;-        }-    }--    // Start from offset equal to end of free space.-    VkDeviceSize resultOffset = resultBaseOffset;--    const VkDeviceSize debugMargin = GetDebugMargin();--    // Apply debugMargin at the end.-    if (debugMargin > 0)-    {-        if (resultOffset < debugMargin)-        {-            return false;-        }-        resultOffset -= debugMargin;-    }--    // Apply alignment.-    resultOffset = VmaAlignDown(resultOffset, allocAlignment);--    // Check next suballocations from 2nd for BufferImageGranularity conflicts.-    // Make bigger alignment if necessary.-    if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())-    {-        bool bufferImageGranularityConflict = false;-        for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )-        {-            const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];-            if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))-            {-                if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))-                {-                    bufferImageGranularityConflict = true;-                    break;-                }-            }-            else-                // Already on previous page.-                break;-        }-        if (bufferImageGranularityConflict)-        {-            resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);-        }-    }--    // There is enough free space.-    const VkDeviceSize endOf1st = !suballocations1st.empty() ?-        suballocations1st.back().offset + suballocations1st.back().size :-        0;-    if (endOf1st + debugMargin <= resultOffset)-    {-        // Check previous suballocations for BufferImageGranularity conflicts.-        // If conflict exists, allocation cannot be made here.-        if (bufferImageGranularity > 1)-        {-            for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )-            {-                const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];-                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))-                {-                    if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))-                    {-                        return false;-                    }-                }-                else-                {-                    // Already on next page.-                    break;-                }-            }-        }--        // All tests passed: Success.-        pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);-        // pAllocationRequest->item unused.-        pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;-        return true;-    }--    return false;-}-#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS-#endif // _VMA_BLOCK_METADATA_LINEAR--#if 0-#ifndef _VMA_BLOCK_METADATA_BUDDY-/*-- GetSize() is the original size of allocated memory block.-- m_UsableSize is this size aligned down to a power of two.-  All allocations and calculations happen relative to m_UsableSize.-- GetUnusableSize() is the difference between them.-  It is reported as separate, unused range, not available for allocations.--Node at level 0 has size = m_UsableSize.-Each next level contains nodes with size 2 times smaller than current level.-m_LevelCount is the maximum number of levels to use in the current object.-*/-class VmaBlockMetadata_Buddy : public VmaBlockMetadata-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Buddy)-public:-    VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,-        VkDeviceSize bufferImageGranularity, bool isVirtual);-    virtual ~VmaBlockMetadata_Buddy();--    size_t GetAllocationCount() const override { return m_AllocationCount; }-    VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); }-    bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; }-    VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; }-    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }-    void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); }--    void Init(VkDeviceSize size) override;-    bool Validate() const override;--    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;-    void AddStatistics(VmaStatistics& inoutStats) const override;--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override;-#endif--    bool CreateAllocationRequest(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        bool upperAddress,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest) override;--    void Alloc(-        const VmaAllocationRequest& request,-        VmaSuballocationType type,-        void* userData) override;--    void Free(VmaAllocHandle allocHandle) override;-    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;-    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;-    VmaAllocHandle GetAllocationListBegin() const override;-    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;-    void Clear() override;-    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;--private:-    static const size_t MAX_LEVELS = 48;--    struct ValidationContext-    {-        size_t calculatedAllocationCount = 0;-        size_t calculatedFreeCount = 0;-        VkDeviceSize calculatedSumFreeSize = 0;-    };-    struct Node-    {-        VkDeviceSize offset;-        enum TYPE-        {-            TYPE_FREE,-            TYPE_ALLOCATION,-            TYPE_SPLIT,-            TYPE_COUNT-        } type;-        Node* parent;-        Node* buddy;--        union-        {-            struct-            {-                Node* prev;-                Node* next;-            } free;-            struct-            {-                void* userData;-            } allocation;-            struct-            {-                Node* leftChild;-            } split;-        };-    };--    // Size of the memory block aligned down to a power of two.-    VkDeviceSize m_UsableSize;-    uint32_t m_LevelCount;-    VmaPoolAllocator<Node> m_NodeAllocator;-    Node* m_Root;-    struct-    {-        Node* front;-        Node* back;-    } m_FreeList[MAX_LEVELS];--    // Number of nodes in the tree with type == TYPE_ALLOCATION.-    size_t m_AllocationCount;-    // Number of nodes in the tree with type == TYPE_FREE.-    size_t m_FreeCount;-    // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes.-    // Doesn't include unusable size.-    VkDeviceSize m_SumFreeSize;--    VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; }-    VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; }--    VkDeviceSize AlignAllocationSize(VkDeviceSize size) const-    {-        if (!IsVirtual())-        {-            size = VmaAlignUp(size, (VkDeviceSize)16);-        }-        return VmaNextPow2(size);-    }-    Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const;-    void DeleteNodeChildren(Node* node);-    bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const;-    uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const;-    void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const;-    // Adds node to the front of FreeList at given level.-    // node->type must be FREE.-    // node->free.prev, next can be undefined.-    void AddToFreeListFront(uint32_t level, Node* node);-    // Removes node from FreeList at given level.-    // node->type must be FREE.-    // node->free.prev, next stay untouched.-    void RemoveFromFreeList(uint32_t level, Node* node);-    void DebugLogAllAllocationNode(Node* node, uint32_t level) const;--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const;-#endif-};--#ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS-VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks,-    VkDeviceSize bufferImageGranularity, bool isVirtual)-    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),-    m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity-    m_Root(VMA_NULL),-    m_AllocationCount(0),-    m_FreeCount(1),-    m_SumFreeSize(0)-{-    memset(m_FreeList, 0, sizeof(m_FreeList));-}--VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy()-{-    DeleteNodeChildren(m_Root);-    m_NodeAllocator.Free(m_Root);-}--void VmaBlockMetadata_Buddy::Init(VkDeviceSize size)-{-    VmaBlockMetadata::Init(size);--    m_UsableSize = VmaPrevPow2(size);-    m_SumFreeSize = m_UsableSize;--    // Calculate m_LevelCount.-    const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16;-    m_LevelCount = 1;-    while (m_LevelCount < MAX_LEVELS &&-        LevelToNodeSize(m_LevelCount) >= minNodeSize)-    {-        ++m_LevelCount;-    }--    Node* rootNode = m_NodeAllocator.Alloc();-    rootNode->offset = 0;-    rootNode->type = Node::TYPE_FREE;-    rootNode->parent = VMA_NULL;-    rootNode->buddy = VMA_NULL;--    m_Root = rootNode;-    AddToFreeListFront(0, rootNode);-}--bool VmaBlockMetadata_Buddy::Validate() const-{-    // Validate tree.-    ValidationContext ctx;-    if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0)))-    {-        VMA_VALIDATE(false && "ValidateNode failed.");-    }-    VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount);-    VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize);--    // Validate free node lists.-    for (uint32_t level = 0; level < m_LevelCount; ++level)-    {-        VMA_VALIDATE(m_FreeList[level].front == VMA_NULL ||-            m_FreeList[level].front->free.prev == VMA_NULL);--        for (Node* node = m_FreeList[level].front;-            node != VMA_NULL;-            node = node->free.next)-        {-            VMA_VALIDATE(node->type == Node::TYPE_FREE);--            if (node->free.next == VMA_NULL)-            {-                VMA_VALIDATE(m_FreeList[level].back == node);-            }-            else-            {-                VMA_VALIDATE(node->free.next->free.prev == node);-            }-        }-    }--    // Validate that free lists ar higher levels are empty.-    for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level)-    {-        VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL);-    }--    return true;-}--void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const-{-    inoutStats.statistics.blockCount++;-    inoutStats.statistics.blockBytes += GetSize();--    AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0));--    const VkDeviceSize unusableSize = GetUnusableSize();-    if (unusableSize > 0)-        VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize);-}--void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const-{-    inoutStats.blockCount++;-    inoutStats.allocationCount += (uint32_t)m_AllocationCount;-    inoutStats.blockBytes += GetSize();-    inoutStats.allocationBytes += GetSize() - m_SumFreeSize;-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const-{-    VmaDetailedStatistics stats;-    VmaClearDetailedStatistics(stats);-    AddDetailedStatistics(stats);--    PrintDetailedMap_Begin(-        json,-        stats.statistics.blockBytes - stats.statistics.allocationBytes,-        stats.statistics.allocationCount,-        stats.unusedRangeCount,-        mapRefCount);--    PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0));--    const VkDeviceSize unusableSize = GetUnusableSize();-    if (unusableSize > 0)-    {-        PrintDetailedMap_UnusedRange(json,-            m_UsableSize, // offset-            unusableSize); // size-    }--    PrintDetailedMap_End(json);-}-#endif // VMA_STATS_STRING_ENABLED--bool VmaBlockMetadata_Buddy::CreateAllocationRequest(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    bool upperAddress,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");--    allocSize = AlignAllocationSize(allocSize);--    // Simple way to respect bufferImageGranularity. May be optimized some day.-    // Whenever it might be an OPTIMAL image...-    if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||-        allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||-        allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)-    {-        allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity());-        allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity());-    }--    if (allocSize > m_UsableSize)-    {-        return false;-    }--    const uint32_t targetLevel = AllocSizeToLevel(allocSize);-    for (uint32_t level = targetLevel; level--; )-    {-        for (Node* freeNode = m_FreeList[level].front;-            freeNode != VMA_NULL;-            freeNode = freeNode->free.next)-        {-            if (freeNode->offset % allocAlignment == 0)-            {-                pAllocationRequest->type = VmaAllocationRequestType::Normal;-                pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1);-                pAllocationRequest->size = allocSize;-                pAllocationRequest->customData = (void*)(uintptr_t)level;-                return true;-            }-        }-    }--    return false;-}--void VmaBlockMetadata_Buddy::Alloc(-    const VmaAllocationRequest& request,-    VmaSuballocationType type,-    void* userData)-{-    VMA_ASSERT(request.type == VmaAllocationRequestType::Normal);--    const uint32_t targetLevel = AllocSizeToLevel(request.size);-    uint32_t currLevel = (uint32_t)(uintptr_t)request.customData;--    Node* currNode = m_FreeList[currLevel].front;-    VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);-    const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;-    while (currNode->offset != offset)-    {-        currNode = currNode->free.next;-        VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE);-    }--    // Go down, splitting free nodes.-    while (currLevel < targetLevel)-    {-        // currNode is already first free node at currLevel.-        // Remove it from list of free nodes at this currLevel.-        RemoveFromFreeList(currLevel, currNode);--        const uint32_t childrenLevel = currLevel + 1;--        // Create two free sub-nodes.-        Node* leftChild = m_NodeAllocator.Alloc();-        Node* rightChild = m_NodeAllocator.Alloc();--        leftChild->offset = currNode->offset;-        leftChild->type = Node::TYPE_FREE;-        leftChild->parent = currNode;-        leftChild->buddy = rightChild;--        rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel);-        rightChild->type = Node::TYPE_FREE;-        rightChild->parent = currNode;-        rightChild->buddy = leftChild;--        // Convert current currNode to split type.-        currNode->type = Node::TYPE_SPLIT;-        currNode->split.leftChild = leftChild;--        // Add child nodes to free list. Order is important!-        AddToFreeListFront(childrenLevel, rightChild);-        AddToFreeListFront(childrenLevel, leftChild);--        ++m_FreeCount;-        ++currLevel;-        currNode = m_FreeList[currLevel].front;--        /*-        We can be sure that currNode, as left child of node previously split,-        also fulfills the alignment requirement.-        */-    }--    // Remove from free list.-    VMA_ASSERT(currLevel == targetLevel &&-        currNode != VMA_NULL &&-        currNode->type == Node::TYPE_FREE);-    RemoveFromFreeList(currLevel, currNode);--    // Convert to allocation node.-    currNode->type = Node::TYPE_ALLOCATION;-    currNode->allocation.userData = userData;--    ++m_AllocationCount;-    --m_FreeCount;-    m_SumFreeSize -= request.size;-}--void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)-{-    uint32_t level = 0;-    outInfo.offset = (VkDeviceSize)allocHandle - 1;-    const Node* const node = FindAllocationNode(outInfo.offset, level);-    outInfo.size = LevelToNodeSize(level);-    outInfo.pUserData = node->allocation.userData;-}--void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const-{-    uint32_t level = 0;-    const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);-    return node->allocation.userData;-}--VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    return VK_NULL_HANDLE;-}--VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const-{-    // Function only used for defragmentation, which is disabled for this algorithm-    return VK_NULL_HANDLE;-}--void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node)-{-    if (node->type == Node::TYPE_SPLIT)-    {-        DeleteNodeChildren(node->split.leftChild->buddy);-        DeleteNodeChildren(node->split.leftChild);-        const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks();-        m_NodeAllocator.Free(node->split.leftChild->buddy);-        m_NodeAllocator.Free(node->split.leftChild);-    }-}--void VmaBlockMetadata_Buddy::Clear()-{-    DeleteNodeChildren(m_Root);-    m_Root->type = Node::TYPE_FREE;-    m_AllocationCount = 0;-    m_FreeCount = 1;-    m_SumFreeSize = m_UsableSize;-}--void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)-{-    uint32_t level = 0;-    Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);-    node->allocation.userData = userData;-}--VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const-{-    Node* node = m_Root;-    VkDeviceSize nodeOffset = 0;-    outLevel = 0;-    VkDeviceSize levelNodeSize = LevelToNodeSize(0);-    while (node->type == Node::TYPE_SPLIT)-    {-        const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1;-        if (offset < nodeOffset + nextLevelNodeSize)-        {-            node = node->split.leftChild;-        }-        else-        {-            node = node->split.leftChild->buddy;-            nodeOffset += nextLevelNodeSize;-        }-        ++outLevel;-        levelNodeSize = nextLevelNodeSize;-    }--    VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION);-    return node;-}--bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const-{-    VMA_VALIDATE(level < m_LevelCount);-    VMA_VALIDATE(curr->parent == parent);-    VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL));-    VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr);-    switch (curr->type)-    {-    case Node::TYPE_FREE:-        // curr->free.prev, next are validated separately.-        ctx.calculatedSumFreeSize += levelNodeSize;-        ++ctx.calculatedFreeCount;-        break;-    case Node::TYPE_ALLOCATION:-        ++ctx.calculatedAllocationCount;-        if (!IsVirtual())-        {-            VMA_VALIDATE(curr->allocation.userData != VMA_NULL);-        }-        break;-    case Node::TYPE_SPLIT:-    {-        const uint32_t childrenLevel = level + 1;-        const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1;-        const Node* const leftChild = curr->split.leftChild;-        VMA_VALIDATE(leftChild != VMA_NULL);-        VMA_VALIDATE(leftChild->offset == curr->offset);-        if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize))-        {-            VMA_VALIDATE(false && "ValidateNode for left child failed.");-        }-        const Node* const rightChild = leftChild->buddy;-        VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize);-        if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize))-        {-            VMA_VALIDATE(false && "ValidateNode for right child failed.");-        }-    }-    break;-    default:-        return false;-    }--    return true;-}--uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const-{-    // I know this could be optimized somehow e.g. by using std::log2p1 from C++20.-    uint32_t level = 0;-    VkDeviceSize currLevelNodeSize = m_UsableSize;-    VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1;-    while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount)-    {-        ++level;-        currLevelNodeSize >>= 1;-        nextLevelNodeSize >>= 1;-    }-    return level;-}--void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle)-{-    uint32_t level = 0;-    Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level);--    ++m_FreeCount;-    --m_AllocationCount;-    m_SumFreeSize += LevelToNodeSize(level);--    node->type = Node::TYPE_FREE;--    // Join free nodes if possible.-    while (level > 0 && node->buddy->type == Node::TYPE_FREE)-    {-        RemoveFromFreeList(level, node->buddy);-        Node* const parent = node->parent;--        m_NodeAllocator.Free(node->buddy);-        m_NodeAllocator.Free(node);-        parent->type = Node::TYPE_FREE;--        node = parent;-        --level;-        --m_FreeCount;-    }--    AddToFreeListFront(level, node);-}--void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const-{-    switch (node->type)-    {-    case Node::TYPE_FREE:-        VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize);-        break;-    case Node::TYPE_ALLOCATION:-        VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize);-        break;-    case Node::TYPE_SPLIT:-    {-        const VkDeviceSize childrenNodeSize = levelNodeSize / 2;-        const Node* const leftChild = node->split.leftChild;-        AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize);-        const Node* const rightChild = leftChild->buddy;-        AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize);-    }-    break;-    default:-        VMA_ASSERT(0);-    }-}--void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node)-{-    VMA_ASSERT(node->type == Node::TYPE_FREE);--    // List is empty.-    Node* const frontNode = m_FreeList[level].front;-    if (frontNode == VMA_NULL)-    {-        VMA_ASSERT(m_FreeList[level].back == VMA_NULL);-        node->free.prev = node->free.next = VMA_NULL;-        m_FreeList[level].front = m_FreeList[level].back = node;-    }-    else-    {-        VMA_ASSERT(frontNode->free.prev == VMA_NULL);-        node->free.prev = VMA_NULL;-        node->free.next = frontNode;-        frontNode->free.prev = node;-        m_FreeList[level].front = node;-    }-}--void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node)-{-    VMA_ASSERT(m_FreeList[level].front != VMA_NULL);--    // It is at the front.-    if (node->free.prev == VMA_NULL)-    {-        VMA_ASSERT(m_FreeList[level].front == node);-        m_FreeList[level].front = node->free.next;-    }-    else-    {-        Node* const prevFreeNode = node->free.prev;-        VMA_ASSERT(prevFreeNode->free.next == node);-        prevFreeNode->free.next = node->free.next;-    }--    // It is at the back.-    if (node->free.next == VMA_NULL)-    {-        VMA_ASSERT(m_FreeList[level].back == node);-        m_FreeList[level].back = node->free.prev;-    }-    else-    {-        Node* const nextFreeNode = node->free.next;-        VMA_ASSERT(nextFreeNode->free.prev == node);-        nextFreeNode->free.prev = node->free.prev;-    }-}--void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const-{-    switch (node->type)-    {-    case Node::TYPE_FREE:-        break;-    case Node::TYPE_ALLOCATION:-        DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData);-        break;-    case Node::TYPE_SPLIT:-    {-        ++level;-        DebugLogAllAllocationNode(node->split.leftChild, level);-        DebugLogAllAllocationNode(node->split.leftChild->buddy, level);-    }-    break;-    default:-        VMA_ASSERT(0);-    }-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const-{-    switch (node->type)-    {-    case Node::TYPE_FREE:-        PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize);-        break;-    case Node::TYPE_ALLOCATION:-        PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData);-        break;-    case Node::TYPE_SPLIT:-    {-        const VkDeviceSize childrenNodeSize = levelNodeSize / 2;-        const Node* const leftChild = node->split.leftChild;-        PrintDetailedMapNode(json, leftChild, childrenNodeSize);-        const Node* const rightChild = leftChild->buddy;-        PrintDetailedMapNode(json, rightChild, childrenNodeSize);-    }-    break;-    default:-        VMA_ASSERT(0);-    }-}-#endif // VMA_STATS_STRING_ENABLED-#endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS-#endif // _VMA_BLOCK_METADATA_BUDDY-#endif // #if 0--#ifndef _VMA_BLOCK_METADATA_TLSF-// To not search current larger region if first allocation won't succeed and skip to smaller range-// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest().-// When fragmentation and reusal of previous blocks doesn't matter then use with-// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible.-class VmaBlockMetadata_TLSF : public VmaBlockMetadata-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_TLSF)-public:-    VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,-        VkDeviceSize bufferImageGranularity, bool isVirtual);-    virtual ~VmaBlockMetadata_TLSF();--    size_t GetAllocationCount() const override { return m_AllocCount; }-    size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }-    VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }-    bool IsEmpty() const override { return m_NullBlock->offset == 0; }-    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }--    void Init(VkDeviceSize size) override;-    bool Validate() const override;--    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;-    void AddStatistics(VmaStatistics& inoutStats) const override;--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json) const override;-#endif--    bool CreateAllocationRequest(-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        bool upperAddress,-        VmaSuballocationType allocType,-        uint32_t strategy,-        VmaAllocationRequest* pAllocationRequest) override;--    VkResult CheckCorruption(const void* pBlockData) override;-    void Alloc(-        const VmaAllocationRequest& request,-        VmaSuballocationType type,-        void* userData) override;--    void Free(VmaAllocHandle allocHandle) override;-    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;-    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;-    VmaAllocHandle GetAllocationListBegin() const override;-    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;-    VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;-    void Clear() override;-    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;-    void DebugLogAllAllocations() const override;--private:-    // According to original paper it should be preferable 4 or 5:-    // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"-    // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf-    static const uint8_t SECOND_LEVEL_INDEX = 5;-    static const uint16_t SMALL_BUFFER_SIZE = 256;-    static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;-    static const uint8_t MEMORY_CLASS_SHIFT = 7;-    static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;--    class Block-    {-    public:-        VkDeviceSize offset;-        VkDeviceSize size;-        Block* prevPhysical;-        Block* nextPhysical;--        void MarkFree() { prevFree = VMA_NULL; }-        void MarkTaken() { prevFree = this; }-        bool IsFree() const { return prevFree != this; }-        void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }-        Block*& PrevFree() { return prevFree; }-        Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }--    private:-        Block* prevFree; // Address of the same block here indicates that block is taken-        union-        {-            Block* nextFree;-            void* userData;-        };-    };--    size_t m_AllocCount;-    // Total number of free blocks besides null block-    size_t m_BlocksFreeCount;-    // Total size of free blocks excluding null block-    VkDeviceSize m_BlocksFreeSize;-    uint32_t m_IsFreeBitmap;-    uint8_t m_MemoryClasses;-    uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];-    uint32_t m_ListsCount;-    /*-    * 0: 0-3 lists for small buffers-    * 1+: 0-(2^SLI-1) lists for normal buffers-    */-    Block** m_FreeList;-    VmaPoolAllocator<Block> m_BlockAllocator;-    Block* m_NullBlock;-    VmaBlockBufferImageGranularity m_GranularityHandler;--    uint8_t SizeToMemoryClass(VkDeviceSize size) const;-    uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;-    uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;-    uint32_t GetListIndex(VkDeviceSize size) const;--    void RemoveFreeBlock(Block* block);-    void InsertFreeBlock(Block* block);-    void MergeBlock(Block* block, Block* prev);--    Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;-    bool CheckBlock(-        Block& block,-        uint32_t listIndex,-        VkDeviceSize allocSize,-        VkDeviceSize allocAlignment,-        VmaSuballocationType allocType,-        VmaAllocationRequest* pAllocationRequest);-};--#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS-VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,-    VkDeviceSize bufferImageGranularity, bool isVirtual)-    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),-    m_AllocCount(0),-    m_BlocksFreeCount(0),-    m_BlocksFreeSize(0),-    m_IsFreeBitmap(0),-    m_MemoryClasses(0),-    m_ListsCount(0),-    m_FreeList(VMA_NULL),-    m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),-    m_NullBlock(VMA_NULL),-    m_GranularityHandler(bufferImageGranularity) {}--VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()-{-    if (m_FreeList)-        vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);-    m_GranularityHandler.Destroy(GetAllocationCallbacks());-}--void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)-{-    VmaBlockMetadata::Init(size);--    if (!IsVirtual())-        m_GranularityHandler.Init(GetAllocationCallbacks(), size);--    m_NullBlock = m_BlockAllocator.Alloc();-    m_NullBlock->size = size;-    m_NullBlock->offset = 0;-    m_NullBlock->prevPhysical = VMA_NULL;-    m_NullBlock->nextPhysical = VMA_NULL;-    m_NullBlock->MarkFree();-    m_NullBlock->NextFree() = VMA_NULL;-    m_NullBlock->PrevFree() = VMA_NULL;-    uint8_t memoryClass = SizeToMemoryClass(size);-    uint16_t sli = SizeToSecondIndex(size, memoryClass);-    m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;-    if (IsVirtual())-        m_ListsCount += 1UL << SECOND_LEVEL_INDEX;-    else-        m_ListsCount += 4;--    m_MemoryClasses = memoryClass + uint8_t(2);-    memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));--    m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);-    memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));-}--bool VmaBlockMetadata_TLSF::Validate() const-{-    VMA_VALIDATE(GetSumFreeSize() <= GetSize());--    VkDeviceSize calculatedSize = m_NullBlock->size;-    VkDeviceSize calculatedFreeSize = m_NullBlock->size;-    size_t allocCount = 0;-    size_t freeCount = 0;--    // Check integrity of free lists-    for (uint32_t list = 0; list < m_ListsCount; ++list)-    {-        Block* block = m_FreeList[list];-        if (block != VMA_NULL)-        {-            VMA_VALIDATE(block->IsFree());-            VMA_VALIDATE(block->PrevFree() == VMA_NULL);-            while (block->NextFree())-            {-                VMA_VALIDATE(block->NextFree()->IsFree());-                VMA_VALIDATE(block->NextFree()->PrevFree() == block);-                block = block->NextFree();-            }-        }-    }--    VkDeviceSize nextOffset = m_NullBlock->offset;-    auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());--    VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);-    if (m_NullBlock->prevPhysical)-    {-        VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);-    }-    // Check all blocks-    for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)-    {-        VMA_VALIDATE(prev->offset + prev->size == nextOffset);-        nextOffset = prev->offset;-        calculatedSize += prev->size;--        uint32_t listIndex = GetListIndex(prev->size);-        if (prev->IsFree())-        {-            ++freeCount;-            // Check if free block belongs to free list-            Block* freeBlock = m_FreeList[listIndex];-            VMA_VALIDATE(freeBlock != VMA_NULL);--            bool found = false;-            do-            {-                if (freeBlock == prev)-                    found = true;--                freeBlock = freeBlock->NextFree();-            } while (!found && freeBlock != VMA_NULL);--            VMA_VALIDATE(found);-            calculatedFreeSize += prev->size;-        }-        else-        {-            ++allocCount;-            // Check if taken block is not on a free list-            Block* freeBlock = m_FreeList[listIndex];-            while (freeBlock)-            {-                VMA_VALIDATE(freeBlock != prev);-                freeBlock = freeBlock->NextFree();-            }--            if (!IsVirtual())-            {-                VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));-            }-        }--        if (prev->prevPhysical)-        {-            VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);-        }-    }--    if (!IsVirtual())-    {-        VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));-    }--    VMA_VALIDATE(nextOffset == 0);-    VMA_VALIDATE(calculatedSize == GetSize());-    VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());-    VMA_VALIDATE(allocCount == m_AllocCount);-    VMA_VALIDATE(freeCount == m_BlocksFreeCount);--    return true;-}--void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const-{-    inoutStats.statistics.blockCount++;-    inoutStats.statistics.blockBytes += GetSize();-    if (m_NullBlock->size > 0)-        VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);--    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)-    {-        if (block->IsFree())-            VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);-        else-            VmaAddDetailedStatisticsAllocation(inoutStats, block->size);-    }-}--void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const-{-    inoutStats.blockCount++;-    inoutStats.allocationCount += (uint32_t)m_AllocCount;-    inoutStats.blockBytes += GetSize();-    inoutStats.allocationBytes += GetSize() - GetSumFreeSize();-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const-{-    size_t blockCount = m_AllocCount + m_BlocksFreeCount;-    VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());-    VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);--    size_t i = blockCount;-    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)-    {-        blockList[--i] = block;-    }-    VMA_ASSERT(i == 0);--    VmaDetailedStatistics stats;-    VmaClearDetailedStatistics(stats);-    AddDetailedStatistics(stats);--    PrintDetailedMap_Begin(json,-        stats.statistics.blockBytes - stats.statistics.allocationBytes,-        stats.statistics.allocationCount,-        stats.unusedRangeCount);--    for (; i < blockCount; ++i)-    {-        Block* block = blockList[i];-        if (block->IsFree())-            PrintDetailedMap_UnusedRange(json, block->offset, block->size);-        else-            PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());-    }-    if (m_NullBlock->size > 0)-        PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);--    PrintDetailedMap_End(json);-}-#endif--bool VmaBlockMetadata_TLSF::CreateAllocationRequest(-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    bool upperAddress,-    VmaSuballocationType allocType,-    uint32_t strategy,-    VmaAllocationRequest* pAllocationRequest)-{-    VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");-    VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");--    // For small granularity round up-    if (!IsVirtual())-        m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);--    allocSize += GetDebugMargin();-    // Quick check for too small pool-    if (allocSize > GetSumFreeSize())-        return false;--    // If no free blocks in pool then check only null block-    if (m_BlocksFreeCount == 0)-        return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);--    // Round up to the next block-    VkDeviceSize sizeForNextList = allocSize;-    VkDeviceSize smallSizeStep = VkDeviceSize(SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4));-    if (allocSize > SMALL_BUFFER_SIZE)-    {-        sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));-    }-    else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)-        sizeForNextList = SMALL_BUFFER_SIZE + 1;-    else-        sizeForNextList += smallSizeStep;--    uint32_t nextListIndex = m_ListsCount;-    uint32_t prevListIndex = m_ListsCount;-    Block* nextListBlock = VMA_NULL;-    Block* prevListBlock = VMA_NULL;--    // Check blocks according to strategies-    if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)-    {-        // Quick check for larger block first-        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);-        if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-            return true;--        // If not fitted then null block-        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))-            return true;--        // Null block failed, search larger bucket-        while (nextListBlock)-        {-            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            nextListBlock = nextListBlock->NextFree();-        }--        // Failed again, check best fit bucket-        prevListBlock = FindFreeBlock(allocSize, prevListIndex);-        while (prevListBlock)-        {-            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            prevListBlock = prevListBlock->NextFree();-        }-    }-    else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)-    {-        // Check best fit bucket-        prevListBlock = FindFreeBlock(allocSize, prevListIndex);-        while (prevListBlock)-        {-            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            prevListBlock = prevListBlock->NextFree();-        }--        // If failed check null block-        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))-            return true;--        // Check larger bucket-        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);-        while (nextListBlock)-        {-            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            nextListBlock = nextListBlock->NextFree();-        }-    }-    else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )-    {-        // Perform search from the start-        VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());-        VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);--        size_t i = m_BlocksFreeCount;-        for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)-        {-            if (block->IsFree() && block->size >= allocSize)-                blockList[--i] = block;-        }--        for (; i < m_BlocksFreeCount; ++i)-        {-            Block& block = *blockList[i];-            if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-        }--        // If failed check null block-        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))-            return true;--        // Whole range searched, no more memory-        return false;-    }-    else-    {-        // Check larger bucket-        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);-        while (nextListBlock)-        {-            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            nextListBlock = nextListBlock->NextFree();-        }--        // If failed check null block-        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))-            return true;--        // Check best fit bucket-        prevListBlock = FindFreeBlock(allocSize, prevListIndex);-        while (prevListBlock)-        {-            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            prevListBlock = prevListBlock->NextFree();-        }-    }--    // Worst case, full search has to be done-    while (++nextListIndex < m_ListsCount)-    {-        nextListBlock = m_FreeList[nextListIndex];-        while (nextListBlock)-        {-            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))-                return true;-            nextListBlock = nextListBlock->NextFree();-        }-    }--    // No more memory sadly-    return false;-}--VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)-{-    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)-    {-        if (!block->IsFree())-        {-            if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))-            {-                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");-                return VK_ERROR_UNKNOWN_COPY;-            }-        }-    }--    return VK_SUCCESS;-}--void VmaBlockMetadata_TLSF::Alloc(-    const VmaAllocationRequest& request,-    VmaSuballocationType type,-    void* userData)-{-    VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);--    // Get block and pop it from the free list-    Block* currentBlock = (Block*)request.allocHandle;-    VkDeviceSize offset = request.algorithmData;-    VMA_ASSERT(currentBlock != VMA_NULL);-    VMA_ASSERT(currentBlock->offset <= offset);--    if (currentBlock != m_NullBlock)-        RemoveFreeBlock(currentBlock);--    VkDeviceSize debugMargin = GetDebugMargin();-    VkDeviceSize misssingAlignment = offset - currentBlock->offset;--    // Append missing alignment to prev block or create new one-    if (misssingAlignment)-    {-        Block* prevBlock = currentBlock->prevPhysical;-        VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");--        if (prevBlock->IsFree() && prevBlock->size != debugMargin)-        {-            uint32_t oldList = GetListIndex(prevBlock->size);-            prevBlock->size += misssingAlignment;-            // Check if new size crosses list bucket-            if (oldList != GetListIndex(prevBlock->size))-            {-                prevBlock->size -= misssingAlignment;-                RemoveFreeBlock(prevBlock);-                prevBlock->size += misssingAlignment;-                InsertFreeBlock(prevBlock);-            }-            else-                m_BlocksFreeSize += misssingAlignment;-        }-        else-        {-            Block* newBlock = m_BlockAllocator.Alloc();-            currentBlock->prevPhysical = newBlock;-            prevBlock->nextPhysical = newBlock;-            newBlock->prevPhysical = prevBlock;-            newBlock->nextPhysical = currentBlock;-            newBlock->size = misssingAlignment;-            newBlock->offset = currentBlock->offset;-            newBlock->MarkTaken();--            InsertFreeBlock(newBlock);-        }--        currentBlock->size -= misssingAlignment;-        currentBlock->offset += misssingAlignment;-    }--    VkDeviceSize size = request.size + debugMargin;-    if (currentBlock->size == size)-    {-        if (currentBlock == m_NullBlock)-        {-            // Setup new null block-            m_NullBlock = m_BlockAllocator.Alloc();-            m_NullBlock->size = 0;-            m_NullBlock->offset = currentBlock->offset + size;-            m_NullBlock->prevPhysical = currentBlock;-            m_NullBlock->nextPhysical = VMA_NULL;-            m_NullBlock->MarkFree();-            m_NullBlock->PrevFree() = VMA_NULL;-            m_NullBlock->NextFree() = VMA_NULL;-            currentBlock->nextPhysical = m_NullBlock;-            currentBlock->MarkTaken();-        }-    }-    else-    {-        VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");--        // Create new free block-        Block* newBlock = m_BlockAllocator.Alloc();-        newBlock->size = currentBlock->size - size;-        newBlock->offset = currentBlock->offset + size;-        newBlock->prevPhysical = currentBlock;-        newBlock->nextPhysical = currentBlock->nextPhysical;-        currentBlock->nextPhysical = newBlock;-        currentBlock->size = size;--        if (currentBlock == m_NullBlock)-        {-            m_NullBlock = newBlock;-            m_NullBlock->MarkFree();-            m_NullBlock->NextFree() = VMA_NULL;-            m_NullBlock->PrevFree() = VMA_NULL;-            currentBlock->MarkTaken();-        }-        else-        {-            newBlock->nextPhysical->prevPhysical = newBlock;-            newBlock->MarkTaken();-            InsertFreeBlock(newBlock);-        }-    }-    currentBlock->UserData() = userData;--    if (debugMargin > 0)-    {-        currentBlock->size -= debugMargin;-        Block* newBlock = m_BlockAllocator.Alloc();-        newBlock->size = debugMargin;-        newBlock->offset = currentBlock->offset + currentBlock->size;-        newBlock->prevPhysical = currentBlock;-        newBlock->nextPhysical = currentBlock->nextPhysical;-        newBlock->MarkTaken();-        currentBlock->nextPhysical->prevPhysical = newBlock;-        currentBlock->nextPhysical = newBlock;-        InsertFreeBlock(newBlock);-    }--    if (!IsVirtual())-        m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,-            currentBlock->offset, currentBlock->size);-    ++m_AllocCount;-}--void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)-{-    Block* block = (Block*)allocHandle;-    Block* next = block->nextPhysical;-    VMA_ASSERT(!block->IsFree() && "Block is already free!");--    if (!IsVirtual())-        m_GranularityHandler.FreePages(block->offset, block->size);-    --m_AllocCount;--    VkDeviceSize debugMargin = GetDebugMargin();-    if (debugMargin > 0)-    {-        RemoveFreeBlock(next);-        MergeBlock(next, block);-        block = next;-        next = next->nextPhysical;-    }--    // Try merging-    Block* prev = block->prevPhysical;-    if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)-    {-        RemoveFreeBlock(prev);-        MergeBlock(block, prev);-    }--    if (!next->IsFree())-        InsertFreeBlock(block);-    else if (next == m_NullBlock)-        MergeBlock(m_NullBlock, block);-    else-    {-        RemoveFreeBlock(next);-        MergeBlock(next, block);-        InsertFreeBlock(next);-    }-}--void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)-{-    Block* block = (Block*)allocHandle;-    VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");-    outInfo.offset = block->offset;-    outInfo.size = block->size;-    outInfo.pUserData = block->UserData();-}--void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const-{-    Block* block = (Block*)allocHandle;-    VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");-    return block->UserData();-}--VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const-{-    if (m_AllocCount == 0)-        return VK_NULL_HANDLE;--    for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)-    {-        if (!block->IsFree())-            return (VmaAllocHandle)block;-    }-    VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");-    return VK_NULL_HANDLE;-}--VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const-{-    Block* startBlock = (Block*)prevAlloc;-    VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");--    for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)-    {-        if (!block->IsFree())-            return (VmaAllocHandle)block;-    }-    return VK_NULL_HANDLE;-}--VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const-{-    Block* block = (Block*)alloc;-    VMA_ASSERT(!block->IsFree() && "Incorrect block!");--    if (block->prevPhysical)-        return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;-    return 0;-}--void VmaBlockMetadata_TLSF::Clear()-{-    m_AllocCount = 0;-    m_BlocksFreeCount = 0;-    m_BlocksFreeSize = 0;-    m_IsFreeBitmap = 0;-    m_NullBlock->offset = 0;-    m_NullBlock->size = GetSize();-    Block* block = m_NullBlock->prevPhysical;-    m_NullBlock->prevPhysical = VMA_NULL;-    while (block)-    {-        Block* prev = block->prevPhysical;-        m_BlockAllocator.Free(block);-        block = prev;-    }-    memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));-    memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));-    m_GranularityHandler.Clear();-}--void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)-{-    Block* block = (Block*)allocHandle;-    VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");-    block->UserData() = userData;-}--void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const-{-    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)-        if (!block->IsFree())-            DebugLogAllocation(block->offset, block->size, block->UserData());-}--uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const-{-    if (size > SMALL_BUFFER_SIZE)-        return uint8_t(VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT);-    return 0;-}--uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const-{-    if (memoryClass == 0)-    {-        if (IsVirtual())-            return static_cast<uint16_t>((size - 1) / 8);-        else-            return static_cast<uint16_t>((size - 1) / 64);-    }-    return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));-}--uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const-{-    if (memoryClass == 0)-        return secondIndex;--    const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;-    if (IsVirtual())-        return index + (1 << SECOND_LEVEL_INDEX);-    else-        return index + 4;-}--uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const-{-    uint8_t memoryClass = SizeToMemoryClass(size);-    return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));-}--void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)-{-    VMA_ASSERT(block != m_NullBlock);-    VMA_ASSERT(block->IsFree());--    if (block->NextFree() != VMA_NULL)-        block->NextFree()->PrevFree() = block->PrevFree();-    if (block->PrevFree() != VMA_NULL)-        block->PrevFree()->NextFree() = block->NextFree();-    else-    {-        uint8_t memClass = SizeToMemoryClass(block->size);-        uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);-        uint32_t index = GetListIndex(memClass, secondIndex);-        VMA_ASSERT(m_FreeList[index] == block);-        m_FreeList[index] = block->NextFree();-        if (block->NextFree() == VMA_NULL)-        {-            m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);-            if (m_InnerIsFreeBitmap[memClass] == 0)-                m_IsFreeBitmap &= ~(1UL << memClass);-        }-    }-    block->MarkTaken();-    block->UserData() = VMA_NULL;-    --m_BlocksFreeCount;-    m_BlocksFreeSize -= block->size;-}--void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)-{-    VMA_ASSERT(block != m_NullBlock);-    VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");--    uint8_t memClass = SizeToMemoryClass(block->size);-    uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);-    uint32_t index = GetListIndex(memClass, secondIndex);-    VMA_ASSERT(index < m_ListsCount);-    block->PrevFree() = VMA_NULL;-    block->NextFree() = m_FreeList[index];-    m_FreeList[index] = block;-    if (block->NextFree() != VMA_NULL)-        block->NextFree()->PrevFree() = block;-    else-    {-        m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;-        m_IsFreeBitmap |= 1UL << memClass;-    }-    ++m_BlocksFreeCount;-    m_BlocksFreeSize += block->size;-}--void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)-{-    VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!");-    VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");--    block->offset = prev->offset;-    block->size += prev->size;-    block->prevPhysical = prev->prevPhysical;-    if (block->prevPhysical)-        block->prevPhysical->nextPhysical = block;-    m_BlockAllocator.Free(prev);-}--VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const-{-    uint8_t memoryClass = SizeToMemoryClass(size);-    uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));-    if (!innerFreeMap)-    {-        // Check higher levels for available blocks-        uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));-        if (!freeMap)-            return VMA_NULL; // No more memory available--        // Find lowest free region-        memoryClass = VMA_BITSCAN_LSB(freeMap);-        innerFreeMap = m_InnerIsFreeBitmap[memoryClass];-        VMA_ASSERT(innerFreeMap != 0);-    }-    // Find lowest free subregion-    listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));-    VMA_ASSERT(m_FreeList[listIndex]);-    return m_FreeList[listIndex];-}--bool VmaBlockMetadata_TLSF::CheckBlock(-    Block& block,-    uint32_t listIndex,-    VkDeviceSize allocSize,-    VkDeviceSize allocAlignment,-    VmaSuballocationType allocType,-    VmaAllocationRequest* pAllocationRequest)-{-    VMA_ASSERT(block.IsFree() && "Block is already taken!");--    VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);-    if (block.size < allocSize + alignedOffset - block.offset)-        return false;--    // Check for granularity conflicts-    if (!IsVirtual() &&-        m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))-        return false;--    // Alloc successful-    pAllocationRequest->type = VmaAllocationRequestType::TLSF;-    pAllocationRequest->allocHandle = (VmaAllocHandle)&block;-    pAllocationRequest->size = allocSize - GetDebugMargin();-    pAllocationRequest->customData = (void*)allocType;-    pAllocationRequest->algorithmData = alignedOffset;--    // Place block at the start of list if it's normal block-    if (listIndex != m_ListsCount && block.PrevFree())-    {-        block.PrevFree()->NextFree() = block.NextFree();-        if (block.NextFree())-            block.NextFree()->PrevFree() = block.PrevFree();-        block.PrevFree() = VMA_NULL;-        block.NextFree() = m_FreeList[listIndex];-        m_FreeList[listIndex] = &block;-        if (block.NextFree())-            block.NextFree()->PrevFree() = &block;-    }--    return true;-}-#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS-#endif // _VMA_BLOCK_METADATA_TLSF--#ifndef _VMA_BLOCK_VECTOR-/*-Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific-Vulkan memory type.--Synchronized internally with a mutex.-*/-class VmaBlockVector-{-    friend struct VmaDefragmentationContext_T;-    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockVector)-public:-    VmaBlockVector(-        VmaAllocator hAllocator,-        VmaPool hParentPool,-        uint32_t memoryTypeIndex,-        VkDeviceSize preferredBlockSize,-        size_t minBlockCount,-        size_t maxBlockCount,-        VkDeviceSize bufferImageGranularity,-        bool explicitBlockSize,-        uint32_t algorithm,-        float priority,-        VkDeviceSize minAllocationAlignment,-        void* pMemoryAllocateNext);-    ~VmaBlockVector();--    VmaAllocator GetAllocator() const { return m_hAllocator; }-    VmaPool GetParentPool() const { return m_hParentPool; }-    bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }-    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }-    VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }-    VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }-    uint32_t GetAlgorithm() const { return m_Algorithm; }-    bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }-    float GetPriority() const { return m_Priority; }-    const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }-    // To be used only while the m_Mutex is locked. Used during defragmentation.-    size_t GetBlockCount() const { return m_Blocks.size(); }-    // To be used only while the m_Mutex is locked. Used during defragmentation.-    VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }-    VMA_RW_MUTEX &GetMutex() { return m_Mutex; }--    VkResult CreateMinBlocks();-    void AddStatistics(VmaStatistics& inoutStats);-    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);-    bool IsEmpty();-    bool IsCorruptionDetectionEnabled() const;--    VkResult Allocate(-        VkDeviceSize size,-        VkDeviceSize alignment,-        const VmaAllocationCreateInfo& createInfo,-        VmaSuballocationType suballocType,-        size_t allocationCount,-        VmaAllocation* pAllocations);--    void Free(const VmaAllocation hAllocation);--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json);-#endif--    VkResult CheckCorruption();--private:-    const VmaAllocator m_hAllocator;-    const VmaPool m_hParentPool;-    const uint32_t m_MemoryTypeIndex;-    const VkDeviceSize m_PreferredBlockSize;-    const size_t m_MinBlockCount;-    const size_t m_MaxBlockCount;-    const VkDeviceSize m_BufferImageGranularity;-    const bool m_ExplicitBlockSize;-    const uint32_t m_Algorithm;-    const float m_Priority;-    const VkDeviceSize m_MinAllocationAlignment;--    void* const m_pMemoryAllocateNext;-    VMA_RW_MUTEX m_Mutex;-    // Incrementally sorted by sumFreeSize, ascending.-    VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;-    uint32_t m_NextBlockId;-    bool m_IncrementalSort = true;--    void SetIncrementalSort(bool val) { m_IncrementalSort = val; }--    VkDeviceSize CalcMaxBlockSize() const;-    // Finds and removes given block from vector.-    void Remove(VmaDeviceMemoryBlock* pBlock);-    // Performs single step in sorting m_Blocks. They may not be fully sorted-    // after this call.-    void IncrementallySortBlocks();-    void SortByFreeSize();--    VkResult AllocatePage(-        VkDeviceSize size,-        VkDeviceSize alignment,-        const VmaAllocationCreateInfo& createInfo,-        VmaSuballocationType suballocType,-        VmaAllocation* pAllocation);--    VkResult AllocateFromBlock(-        VmaDeviceMemoryBlock* pBlock,-        VkDeviceSize size,-        VkDeviceSize alignment,-        VmaAllocationCreateFlags allocFlags,-        void* pUserData,-        VmaSuballocationType suballocType,-        uint32_t strategy,-        VmaAllocation* pAllocation);--    VkResult CommitAllocationRequest(-        VmaAllocationRequest& allocRequest,-        VmaDeviceMemoryBlock* pBlock,-        VkDeviceSize alignment,-        VmaAllocationCreateFlags allocFlags,-        void* pUserData,-        VmaSuballocationType suballocType,-        VmaAllocation* pAllocation);--    VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);-    bool HasEmptyBlock();-};-#endif // _VMA_BLOCK_VECTOR--#ifndef _VMA_DEFRAGMENTATION_CONTEXT-struct VmaDefragmentationContext_T-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaDefragmentationContext_T)-public:-    VmaDefragmentationContext_T(-        VmaAllocator hAllocator,-        const VmaDefragmentationInfo& info);-    ~VmaDefragmentationContext_T();--    void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }--    VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);-    VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);--private:-    // Max number of allocations to ignore due to size constraints before ending single pass-    static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;-    enum class CounterStatus { Pass, Ignore, End };--    struct FragmentedBlock-    {-        uint32_t data;-        VmaDeviceMemoryBlock* block;-    };-    struct StateBalanced-    {-        VkDeviceSize avgFreeSize = 0;-        VkDeviceSize avgAllocSize = UINT64_MAX;-    };-    struct StateExtensive-    {-        enum class Operation : uint8_t-        {-            FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,-            MoveBuffers, MoveTextures, MoveAll,-            Cleanup, Done-        };--        Operation operation = Operation::FindFreeBlockTexture;-        size_t firstFreeBlock = SIZE_MAX;-    };-    struct MoveAllocationData-    {-        VkDeviceSize size;-        VkDeviceSize alignment;-        VmaSuballocationType type;-        VmaAllocationCreateFlags flags;-        VmaDefragmentationMove move = {};-    };--    const VkDeviceSize m_MaxPassBytes;-    const uint32_t m_MaxPassAllocations;-    const PFN_vmaCheckDefragmentationBreakFunction m_BreakCallback;-    void* m_BreakCallbackUserData;--    VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;-    VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;--    uint8_t m_IgnoredAllocs = 0;-    uint32_t m_Algorithm;-    uint32_t m_BlockVectorCount;-    VmaBlockVector* m_PoolBlockVector;-    VmaBlockVector** m_pBlockVectors;-    size_t m_ImmovableBlockCount = 0;-    VmaDefragmentationStats m_GlobalStats = { 0 };-    VmaDefragmentationStats m_PassStats = { 0 };-    void* m_AlgorithmState = VMA_NULL;--    static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);-    CounterStatus CheckCounters(VkDeviceSize bytes);-    bool IncrementCounters(VkDeviceSize bytes);-    bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);-    bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);--    bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);-    bool ComputeDefragmentation_Fast(VmaBlockVector& vector);-    bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);-    bool ComputeDefragmentation_Full(VmaBlockVector& vector);-    bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);--    void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);-    bool MoveDataToFreeBlocks(VmaSuballocationType currentType,-        VmaBlockVector& vector, size_t firstFreeBlock,-        bool& texturePresent, bool& bufferPresent, bool& otherPresent);-};-#endif // _VMA_DEFRAGMENTATION_CONTEXT--#ifndef _VMA_POOL_T-struct VmaPool_T-{-    friend struct VmaPoolListItemTraits;-    VMA_CLASS_NO_COPY_NO_MOVE(VmaPool_T)-public:-    VmaBlockVector m_BlockVector;-    VmaDedicatedAllocationList m_DedicatedAllocations;--    VmaPool_T(-        VmaAllocator hAllocator,-        const VmaPoolCreateInfo& createInfo,-        VkDeviceSize preferredBlockSize);-    ~VmaPool_T();--    uint32_t GetId() const { return m_Id; }-    void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }--    const char* GetName() const { return m_Name; }-    void SetName(const char* pName);--#if VMA_STATS_STRING_ENABLED-    //void PrintDetailedMap(class VmaStringBuilder& sb);-#endif--private:-    uint32_t m_Id;-    char* m_Name;-    VmaPool_T* m_PrevPool = VMA_NULL;-    VmaPool_T* m_NextPool = VMA_NULL;-};--struct VmaPoolListItemTraits-{-    typedef VmaPool_T ItemType;--    static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }-    static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }-    static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }-    static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }-};-#endif // _VMA_POOL_T--#ifndef _VMA_CURRENT_BUDGET_DATA-struct VmaCurrentBudgetData-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaCurrentBudgetData)-public:--    VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];-    VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];-    VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];-    VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];--#if VMA_MEMORY_BUDGET-    VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;-    VMA_RW_MUTEX m_BudgetMutex;-    uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];-    uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];-    uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];-#endif // VMA_MEMORY_BUDGET--    VmaCurrentBudgetData();--    void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);-    void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);-};--#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS-VmaCurrentBudgetData::VmaCurrentBudgetData()-{-    for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)-    {-        m_BlockCount[heapIndex] = 0;-        m_AllocationCount[heapIndex] = 0;-        m_BlockBytes[heapIndex] = 0;-        m_AllocationBytes[heapIndex] = 0;-#if VMA_MEMORY_BUDGET-        m_VulkanUsage[heapIndex] = 0;-        m_VulkanBudget[heapIndex] = 0;-        m_BlockBytesAtBudgetFetch[heapIndex] = 0;-#endif-    }--#if VMA_MEMORY_BUDGET-    m_OperationsSinceBudgetFetch = 0;-#endif-}--void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)-{-    m_AllocationBytes[heapIndex] += allocationSize;-    ++m_AllocationCount[heapIndex];-#if VMA_MEMORY_BUDGET-    ++m_OperationsSinceBudgetFetch;-#endif-}--void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)-{-    VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);-    m_AllocationBytes[heapIndex] -= allocationSize;-    VMA_ASSERT(m_AllocationCount[heapIndex] > 0);-    --m_AllocationCount[heapIndex];-#if VMA_MEMORY_BUDGET-    ++m_OperationsSinceBudgetFetch;-#endif-}-#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS-#endif // _VMA_CURRENT_BUDGET_DATA--#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR-/*-Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.-*/-class VmaAllocationObjectAllocator-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocationObjectAllocator)-public:-    VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)-        : m_Allocator(pAllocationCallbacks, 1024) {}--    template<typename... Types> VmaAllocation Allocate(Types&&... args);-    void Free(VmaAllocation hAlloc);--private:-    VMA_MUTEX m_Mutex;-    VmaPoolAllocator<VmaAllocation_T> m_Allocator;-};--template<typename... Types>-VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)-{-    VmaMutexLock mutexLock(m_Mutex);-    return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);-}--void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)-{-    VmaMutexLock mutexLock(m_Mutex);-    m_Allocator.Free(hAlloc);-}-#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR--#ifndef _VMA_VIRTUAL_BLOCK_T-struct VmaVirtualBlock_T-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaVirtualBlock_T)-public:-    const bool m_AllocationCallbacksSpecified;-    const VkAllocationCallbacks m_AllocationCallbacks;--    VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);-    ~VmaVirtualBlock_T();--    VkResult Init() { return VK_SUCCESS; }-    bool IsEmpty() const { return m_Metadata->IsEmpty(); }-    void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }-    void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }-    void Clear() { m_Metadata->Clear(); }--    const VkAllocationCallbacks* GetAllocationCallbacks() const;-    void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);-    VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,-        VkDeviceSize* outOffset);-    void GetStatistics(VmaStatistics& outStats) const;-    void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;-#if VMA_STATS_STRING_ENABLED-    void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;-#endif--private:-    VmaBlockMetadata* m_Metadata;-};--#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS-VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)-    : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),-    m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)-{-    const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;-    switch (algorithm)-    {-    case 0:-        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);-        break;-    case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:-        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);-        break;-    default:-        VMA_ASSERT(0);-        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);-    }--    m_Metadata->Init(createInfo.size);-}--VmaVirtualBlock_T::~VmaVirtualBlock_T()-{-    // Define macro VMA_DEBUG_LOG_FORMAT to receive the list of the unfreed allocations-    if (!m_Metadata->IsEmpty())-        m_Metadata->DebugLogAllAllocations();-    // This is the most important assert in the entire library.-    // Hitting it means you have some memory leak - unreleased virtual allocations.-    VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");--    vma_delete(GetAllocationCallbacks(), m_Metadata);-}--const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const-{-    return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;-}--void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)-{-    m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);-}--VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,-    VkDeviceSize* outOffset)-{-    VmaAllocationRequest request = {};-    if (m_Metadata->CreateAllocationRequest(-        createInfo.size, // allocSize-        VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment-        (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress-        VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant-        createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy-        &request))-    {-        m_Metadata->Alloc(request,-            VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant-            createInfo.pUserData);-        outAllocation = (VmaVirtualAllocation)request.allocHandle;-        if(outOffset)-            *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);-        return VK_SUCCESS;-    }-    outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;-    if (outOffset)-        *outOffset = UINT64_MAX;-    return VK_ERROR_OUT_OF_DEVICE_MEMORY;-}--void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const-{-    VmaClearStatistics(outStats);-    m_Metadata->AddStatistics(outStats);-}--void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const-{-    VmaClearDetailedStatistics(outStats);-    m_Metadata->AddDetailedStatistics(outStats);-}--#if VMA_STATS_STRING_ENABLED-void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const-{-    VmaJsonWriter json(GetAllocationCallbacks(), sb);-    json.BeginObject();--    VmaDetailedStatistics stats;-    CalculateDetailedStatistics(stats);--    json.WriteString("Stats");-    VmaPrintDetailedStatistics(json, stats);--    if (detailedMap)-    {-        json.WriteString("Details");-        json.BeginObject();-        m_Metadata->PrintDetailedMap(json);-        json.EndObject();-    }--    json.EndObject();-}-#endif // VMA_STATS_STRING_ENABLED-#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS-#endif // _VMA_VIRTUAL_BLOCK_T---// Main allocator object.-struct VmaAllocator_T-{-    VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocator_T)-public:-    bool m_UseMutex;-    uint32_t m_VulkanApiVersion;-    bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).-    bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).-    bool m_UseExtMemoryBudget;-    bool m_UseAmdDeviceCoherentMemory;-    bool m_UseKhrBufferDeviceAddress;-    bool m_UseExtMemoryPriority;-    VkDevice m_hDevice;-    VkInstance m_hInstance;-    bool m_AllocationCallbacksSpecified;-    VkAllocationCallbacks m_AllocationCallbacks;-    VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;-    VmaAllocationObjectAllocator m_AllocationObjectAllocator;--    // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.-    uint32_t m_HeapSizeLimitMask;--    VkPhysicalDeviceProperties m_PhysicalDeviceProperties;-    VkPhysicalDeviceMemoryProperties m_MemProps;--    // Default pools.-    VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];-    VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];--    VmaCurrentBudgetData m_Budget;-    VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.--    VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);-    VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);-    ~VmaAllocator_T();--    const VkAllocationCallbacks* GetAllocationCallbacks() const-    {-        return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;-    }-    const VmaVulkanFunctions& GetVulkanFunctions() const-    {-        return m_VulkanFunctions;-    }--    VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }--    VkDeviceSize GetBufferImageGranularity() const-    {-        return VMA_MAX(-            static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),-            m_PhysicalDeviceProperties.limits.bufferImageGranularity);-    }--    uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }-    uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }--    uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const-    {-        VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);-        return m_MemProps.memoryTypes[memTypeIndex].heapIndex;-    }-    // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.-    bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const-    {-        return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==-            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-    }-    // Minimum alignment for all allocations in specific memory type.-    VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const-    {-        return IsMemoryTypeNonCoherent(memTypeIndex) ?-            VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :-            (VkDeviceSize)VMA_MIN_ALIGNMENT;-    }--    bool IsIntegratedGpu() const-    {-        return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;-    }--    uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }--    void GetBufferMemoryRequirements(-        VkBuffer hBuffer,-        VkMemoryRequirements& memReq,-        bool& requiresDedicatedAllocation,-        bool& prefersDedicatedAllocation) const;-    void GetImageMemoryRequirements(-        VkImage hImage,-        VkMemoryRequirements& memReq,-        bool& requiresDedicatedAllocation,-        bool& prefersDedicatedAllocation) const;-    VkResult FindMemoryTypeIndex(-        uint32_t memoryTypeBits,-        const VmaAllocationCreateInfo* pAllocationCreateInfo,-        VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown.-        uint32_t* pMemoryTypeIndex) const;--    // Main allocation function.-    VkResult AllocateMemory(-        const VkMemoryRequirements& vkMemReq,-        bool requiresDedicatedAllocation,-        bool prefersDedicatedAllocation,-        VkBuffer dedicatedBuffer,-        VkImage dedicatedImage,-        VkFlags dedicatedBufferImageUsage, // UINT32_MAX if unknown.-        const VmaAllocationCreateInfo& createInfo,-        VmaSuballocationType suballocType,-        size_t allocationCount,-        VmaAllocation* pAllocations);--    // Main deallocation function.-    void FreeMemory(-        size_t allocationCount,-        const VmaAllocation* pAllocations);--    void CalculateStatistics(VmaTotalStatistics* pStats);--    void GetHeapBudgets(-        VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);--#if VMA_STATS_STRING_ENABLED-    void PrintDetailedMap(class VmaJsonWriter& json);-#endif--    void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);--    VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);-    void DestroyPool(VmaPool pool);-    void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);-    void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);--    void SetCurrentFrameIndex(uint32_t frameIndex);-    uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }--    VkResult CheckPoolCorruption(VmaPool hPool);-    VkResult CheckCorruption(uint32_t memoryTypeBits);--    // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.-    VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);-    // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.-    void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);-    // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.-    VkResult BindVulkanBuffer(-        VkDeviceMemory memory,-        VkDeviceSize memoryOffset,-        VkBuffer buffer,-        const void* pNext);-    // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.-    VkResult BindVulkanImage(-        VkDeviceMemory memory,-        VkDeviceSize memoryOffset,-        VkImage image,-        const void* pNext);--    VkResult Map(VmaAllocation hAllocation, void** ppData);-    void Unmap(VmaAllocation hAllocation);--    VkResult BindBufferMemory(-        VmaAllocation hAllocation,-        VkDeviceSize allocationLocalOffset,-        VkBuffer hBuffer,-        const void* pNext);-    VkResult BindImageMemory(-        VmaAllocation hAllocation,-        VkDeviceSize allocationLocalOffset,-        VkImage hImage,-        const void* pNext);--    VkResult FlushOrInvalidateAllocation(-        VmaAllocation hAllocation,-        VkDeviceSize offset, VkDeviceSize size,-        VMA_CACHE_OPERATION op);-    VkResult FlushOrInvalidateAllocations(-        uint32_t allocationCount,-        const VmaAllocation* allocations,-        const VkDeviceSize* offsets, const VkDeviceSize* sizes,-        VMA_CACHE_OPERATION op);--    void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);--    /*-    Returns bit mask of memory types that can support defragmentation on GPU as-    they support creation of required buffer for copy operations.-    */-    uint32_t GetGpuDefragmentationMemoryTypeBits();--#if VMA_EXTERNAL_MEMORY-    VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const-    {-        return m_TypeExternalMemoryHandleTypes[memTypeIndex];-    }-#endif // #if VMA_EXTERNAL_MEMORY--private:-    VkDeviceSize m_PreferredLargeHeapBlockSize;--    VkPhysicalDevice m_PhysicalDevice;-    VMA_ATOMIC_UINT32 m_CurrentFrameIndex;-    VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.-#if VMA_EXTERNAL_MEMORY-    VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];-#endif // #if VMA_EXTERNAL_MEMORY--    VMA_RW_MUTEX m_PoolsMutex;-    typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;-    // Protected by m_PoolsMutex.-    PoolList m_Pools;-    uint32_t m_NextPoolId;--    VmaVulkanFunctions m_VulkanFunctions;--    // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.-    uint32_t m_GlobalMemoryTypeBits;--    void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);--#if VMA_STATIC_VULKAN_FUNCTIONS == 1-    void ImportVulkanFunctions_Static();-#endif--    void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);--#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1-    void ImportVulkanFunctions_Dynamic();-#endif--    void ValidateVulkanFunctions();--    VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);--    VkResult AllocateMemoryOfType(-        VmaPool pool,-        VkDeviceSize size,-        VkDeviceSize alignment,-        bool dedicatedPreferred,-        VkBuffer dedicatedBuffer,-        VkImage dedicatedImage,-        VkFlags dedicatedBufferImageUsage,-        const VmaAllocationCreateInfo& createInfo,-        uint32_t memTypeIndex,-        VmaSuballocationType suballocType,-        VmaDedicatedAllocationList& dedicatedAllocations,-        VmaBlockVector& blockVector,-        size_t allocationCount,-        VmaAllocation* pAllocations);--    // Helper function only to be used inside AllocateDedicatedMemory.-    VkResult AllocateDedicatedMemoryPage(-        VmaPool pool,-        VkDeviceSize size,-        VmaSuballocationType suballocType,-        uint32_t memTypeIndex,-        const VkMemoryAllocateInfo& allocInfo,-        bool map,-        bool isUserDataString,-        bool isMappingAllowed,-        void* pUserData,-        VmaAllocation* pAllocation);--    // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.-    VkResult AllocateDedicatedMemory(-        VmaPool pool,-        VkDeviceSize size,-        VmaSuballocationType suballocType,-        VmaDedicatedAllocationList& dedicatedAllocations,-        uint32_t memTypeIndex,-        bool map,-        bool isUserDataString,-        bool isMappingAllowed,-        bool canAliasMemory,-        void* pUserData,-        float priority,-        VkBuffer dedicatedBuffer,-        VkImage dedicatedImage,-        VkFlags dedicatedBufferImageUsage,-        size_t allocationCount,-        VmaAllocation* pAllocations,-        const void* pNextChain = nullptr);--    void FreeDedicatedMemory(const VmaAllocation allocation);--    VkResult CalcMemTypeParams(-        VmaAllocationCreateInfo& outCreateInfo,-        uint32_t memTypeIndex,-        VkDeviceSize size,-        size_t allocationCount);-    VkResult CalcAllocationParams(-        VmaAllocationCreateInfo& outCreateInfo,-        bool dedicatedRequired,-        bool dedicatedPreferred);--    /*-    Calculates and returns bit mask of memory types that can support defragmentation-    on GPU as they support creation of required buffer for copy operations.-    */-    uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;-    uint32_t CalculateGlobalMemoryTypeBits() const;--    bool GetFlushOrInvalidateRange(-        VmaAllocation allocation,-        VkDeviceSize offset, VkDeviceSize size,-        VkMappedMemoryRange& outRange) const;--#if VMA_MEMORY_BUDGET-    void UpdateVulkanBudget();-#endif // #if VMA_MEMORY_BUDGET-};---#ifndef _VMA_MEMORY_FUNCTIONS-static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)-{-    return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);-}--static void VmaFree(VmaAllocator hAllocator, void* ptr)-{-    VmaFree(&hAllocator->m_AllocationCallbacks, ptr);-}--template<typename T>-static T* VmaAllocate(VmaAllocator hAllocator)-{-    return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));-}--template<typename T>-static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)-{-    return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));-}--template<typename T>-static void vma_delete(VmaAllocator hAllocator, T* ptr)-{-    if(ptr != VMA_NULL)-    {-        ptr->~T();-        VmaFree(hAllocator, ptr);-    }-}--template<typename T>-static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)-{-    if(ptr != VMA_NULL)-    {-        for(size_t i = count; i--; )-            ptr[i].~T();-        VmaFree(hAllocator, ptr);-    }-}-#endif // _VMA_MEMORY_FUNCTIONS--#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS-VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)-    : m_pMetadata(VMA_NULL),-    m_MemoryTypeIndex(UINT32_MAX),-    m_Id(0),-    m_hMemory(VK_NULL_HANDLE),-    m_MapCount(0),-    m_pMappedData(VMA_NULL) {}--VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()-{-    VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");-    VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);-}--void VmaDeviceMemoryBlock::Init(-    VmaAllocator hAllocator,-    VmaPool hParentPool,-    uint32_t newMemoryTypeIndex,-    VkDeviceMemory newMemory,-    VkDeviceSize newSize,-    uint32_t id,-    uint32_t algorithm,-    VkDeviceSize bufferImageGranularity)-{-    VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);--    m_hParentPool = hParentPool;-    m_MemoryTypeIndex = newMemoryTypeIndex;-    m_Id = id;-    m_hMemory = newMemory;--    switch (algorithm)-    {-    case 0:-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),-            bufferImageGranularity, false); // isVirtual-        break;-    case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),-            bufferImageGranularity, false); // isVirtual-        break;-    default:-        VMA_ASSERT(0);-        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),-            bufferImageGranularity, false); // isVirtual-    }-    m_pMetadata->Init(newSize);-}--void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)-{-    // Define macro VMA_DEBUG_LOG_FORMAT to receive the list of the unfreed allocations-    if (!m_pMetadata->IsEmpty())-        m_pMetadata->DebugLogAllAllocations();-    // This is the most important assert in the entire library.-    // Hitting it means you have some memory leak - unreleased VmaAllocation objects.-    VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");--    VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);-    allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);-    m_hMemory = VK_NULL_HANDLE;--    vma_delete(allocator, m_pMetadata);-    m_pMetadata = VMA_NULL;-}--void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator)-{-    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    m_MappingHysteresis.PostAlloc();-}--void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)-{-    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    if(m_MappingHysteresis.PostFree())-    {-        VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);-        if (m_MapCount == 0)-        {-            m_pMappedData = VMA_NULL;-            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);-        }-    }-}--bool VmaDeviceMemoryBlock::Validate() const-{-    VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&-        (m_pMetadata->GetSize() != 0));--    return m_pMetadata->Validate();-}--VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)-{-    void* pData = nullptr;-    VkResult res = Map(hAllocator, 1, &pData);-    if (res != VK_SUCCESS)-    {-        return res;-    }--    res = m_pMetadata->CheckCorruption(pData);--    Unmap(hAllocator, 1);--    return res;-}--VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)-{-    if (count == 0)-    {-        return VK_SUCCESS;-    }--    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();-    m_MappingHysteresis.PostMap();-    if (oldTotalMapCount != 0)-    {-        m_MapCount += count;-        VMA_ASSERT(m_pMappedData != VMA_NULL);-        if (ppData != VMA_NULL)-        {-            *ppData = m_pMappedData;-        }-        return VK_SUCCESS;-    }-    else-    {-        VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(-            hAllocator->m_hDevice,-            m_hMemory,-            0, // offset-            VK_WHOLE_SIZE,-            0, // flags-            &m_pMappedData);-        if (result == VK_SUCCESS)-        {-            if (ppData != VMA_NULL)-            {-                *ppData = m_pMappedData;-            }-            m_MapCount = count;-        }-        return result;-    }-}--void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)-{-    if (count == 0)-    {-        return;-    }--    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    if (m_MapCount >= count)-    {-        m_MapCount -= count;-        const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();-        if (totalMapCount == 0)-        {-            m_pMappedData = VMA_NULL;-            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);-        }-        m_MappingHysteresis.PostUnmap();-    }-    else-    {-        VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");-    }-}--VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)-{-    VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);--    void* pData;-    VkResult res = Map(hAllocator, 1, &pData);-    if (res != VK_SUCCESS)-    {-        return res;-    }--    VmaWriteMagicValue(pData, allocOffset + allocSize);--    Unmap(hAllocator, 1);-    return VK_SUCCESS;-}--VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)-{-    VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);--    void* pData;-    VkResult res = Map(hAllocator, 1, &pData);-    if (res != VK_SUCCESS)-    {-        return res;-    }--    if (!VmaValidateMagicValue(pData, allocOffset + allocSize))-    {-        VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");-    }--    Unmap(hAllocator, 1);-    return VK_SUCCESS;-}--VkResult VmaDeviceMemoryBlock::BindBufferMemory(-    const VmaAllocator hAllocator,-    const VmaAllocation hAllocation,-    VkDeviceSize allocationLocalOffset,-    VkBuffer hBuffer,-    const void* pNext)-{-    VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&-        hAllocation->GetBlock() == this);-    VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&-        "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");-    const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;-    // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.-    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);-}--VkResult VmaDeviceMemoryBlock::BindImageMemory(-    const VmaAllocator hAllocator,-    const VmaAllocation hAllocation,-    VkDeviceSize allocationLocalOffset,-    VkImage hImage,-    const void* pNext)-{-    VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&-        hAllocation->GetBlock() == this);-    VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&-        "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");-    const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;-    // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.-    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);-    return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);-}-#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS--#ifndef _VMA_ALLOCATION_T_FUNCTIONS-VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)-    : m_Alignment{ 1 },-    m_Size{ 0 },-    m_pUserData{ VMA_NULL },-    m_pName{ VMA_NULL },-    m_MemoryTypeIndex{ 0 },-    m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },-    m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },-    m_MapCount{ 0 },-    m_Flags{ 0 }-{-    if(mappingAllowed)-        m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;--#if VMA_STATS_STRING_ENABLED-    m_BufferImageUsage = 0;-#endif-}--VmaAllocation_T::~VmaAllocation_T()-{-    VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction.");--    // Check if owned string was freed.-    VMA_ASSERT(m_pName == VMA_NULL);-}--void VmaAllocation_T::InitBlockAllocation(-    VmaDeviceMemoryBlock* block,-    VmaAllocHandle allocHandle,-    VkDeviceSize alignment,-    VkDeviceSize size,-    uint32_t memoryTypeIndex,-    VmaSuballocationType suballocationType,-    bool mapped)-{-    VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);-    VMA_ASSERT(block != VMA_NULL);-    m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;-    m_Alignment = alignment;-    m_Size = size;-    m_MemoryTypeIndex = memoryTypeIndex;-    if(mapped)-    {-        VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");-        m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;-    }-    m_SuballocationType = (uint8_t)suballocationType;-    m_BlockAllocation.m_Block = block;-    m_BlockAllocation.m_AllocHandle = allocHandle;-}--void VmaAllocation_T::InitDedicatedAllocation(-    VmaPool hParentPool,-    uint32_t memoryTypeIndex,-    VkDeviceMemory hMemory,-    VmaSuballocationType suballocationType,-    void* pMappedData,-    VkDeviceSize size)-{-    VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);-    VMA_ASSERT(hMemory != VK_NULL_HANDLE);-    m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;-    m_Alignment = 0;-    m_Size = size;-    m_MemoryTypeIndex = memoryTypeIndex;-    m_SuballocationType = (uint8_t)suballocationType;-    if(pMappedData != VMA_NULL)-    {-        VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");-        m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;-    }-    m_DedicatedAllocation.m_hParentPool = hParentPool;-    m_DedicatedAllocation.m_hMemory = hMemory;-    m_DedicatedAllocation.m_pMappedData = pMappedData;-    m_DedicatedAllocation.m_Prev = VMA_NULL;-    m_DedicatedAllocation.m_Next = VMA_NULL;-}--void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)-{-    VMA_ASSERT(pName == VMA_NULL || pName != m_pName);--    FreeName(hAllocator);--    if (pName != VMA_NULL)-        m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);-}--uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)-{-    VMA_ASSERT(allocation != VMA_NULL);-    VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);-    VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);--    if (m_MapCount != 0)-        m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);--    m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);-    VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation);-    m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);--#if VMA_STATS_STRING_ENABLED-    VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage);-#endif-    return m_MapCount;-}--VmaAllocHandle VmaAllocation_T::GetAllocHandle() const-{-    switch (m_Type)-    {-    case ALLOCATION_TYPE_BLOCK:-        return m_BlockAllocation.m_AllocHandle;-    case ALLOCATION_TYPE_DEDICATED:-        return VK_NULL_HANDLE;-    default:-        VMA_ASSERT(0);-        return VK_NULL_HANDLE;-    }-}--VkDeviceSize VmaAllocation_T::GetOffset() const-{-    switch (m_Type)-    {-    case ALLOCATION_TYPE_BLOCK:-        return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);-    case ALLOCATION_TYPE_DEDICATED:-        return 0;-    default:-        VMA_ASSERT(0);-        return 0;-    }-}--VmaPool VmaAllocation_T::GetParentPool() const-{-    switch (m_Type)-    {-    case ALLOCATION_TYPE_BLOCK:-        return m_BlockAllocation.m_Block->GetParentPool();-    case ALLOCATION_TYPE_DEDICATED:-        return m_DedicatedAllocation.m_hParentPool;-    default:-        VMA_ASSERT(0);-        return VK_NULL_HANDLE;-    }-}--VkDeviceMemory VmaAllocation_T::GetMemory() const-{-    switch (m_Type)-    {-    case ALLOCATION_TYPE_BLOCK:-        return m_BlockAllocation.m_Block->GetDeviceMemory();-    case ALLOCATION_TYPE_DEDICATED:-        return m_DedicatedAllocation.m_hMemory;-    default:-        VMA_ASSERT(0);-        return VK_NULL_HANDLE;-    }-}--void* VmaAllocation_T::GetMappedData() const-{-    switch (m_Type)-    {-    case ALLOCATION_TYPE_BLOCK:-        if (m_MapCount != 0 || IsPersistentMap())-        {-            void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();-            VMA_ASSERT(pBlockData != VMA_NULL);-            return (char*)pBlockData + GetOffset();-        }-        else-        {-            return VMA_NULL;-        }-        break;-    case ALLOCATION_TYPE_DEDICATED:-        VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap()));-        return m_DedicatedAllocation.m_pMappedData;-    default:-        VMA_ASSERT(0);-        return VMA_NULL;-    }-}--void VmaAllocation_T::BlockAllocMap()-{-    VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);-    VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");--    if (m_MapCount < 0xFF)-    {-        ++m_MapCount;-    }-    else-    {-        VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");-    }-}--void VmaAllocation_T::BlockAllocUnmap()-{-    VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);--    if (m_MapCount > 0)-    {-        --m_MapCount;-    }-    else-    {-        VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");-    }-}--VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)-{-    VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);-    VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");--    if (m_MapCount != 0 || IsPersistentMap())-    {-        if (m_MapCount < 0xFF)-        {-            VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL);-            *ppData = m_DedicatedAllocation.m_pMappedData;-            ++m_MapCount;-            return VK_SUCCESS;-        }-        else-        {-            VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");-            return VK_ERROR_MEMORY_MAP_FAILED;-        }-    }-    else-    {-        VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(-            hAllocator->m_hDevice,-            m_DedicatedAllocation.m_hMemory,-            0, // offset-            VK_WHOLE_SIZE,-            0, // flags-            ppData);-        if (result == VK_SUCCESS)-        {-            m_DedicatedAllocation.m_pMappedData = *ppData;-            m_MapCount = 1;-        }-        return result;-    }-}--void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)-{-    VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);--    if (m_MapCount > 0)-    {-        --m_MapCount;-        if (m_MapCount == 0 && !IsPersistentMap())-        {-            m_DedicatedAllocation.m_pMappedData = VMA_NULL;-            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(-                hAllocator->m_hDevice,-                m_DedicatedAllocation.m_hMemory);-        }-    }-    else-    {-        VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");-    }-}--#if VMA_STATS_STRING_ENABLED-void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage)-{-    VMA_ASSERT(m_BufferImageUsage == 0);-    m_BufferImageUsage = bufferImageUsage;-}--void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const-{-    json.WriteString("Type");-    json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);--    json.WriteString("Size");-    json.WriteNumber(m_Size);-    json.WriteString("Usage");-    json.WriteNumber(m_BufferImageUsage);--    if (m_pUserData != VMA_NULL)-    {-        json.WriteString("CustomData");-        json.BeginString();-        json.ContinueString_Pointer(m_pUserData);-        json.EndString();-    }-    if (m_pName != VMA_NULL)-    {-        json.WriteString("Name");-        json.WriteString(m_pName);-    }-}-#endif // VMA_STATS_STRING_ENABLED--void VmaAllocation_T::FreeName(VmaAllocator hAllocator)-{-    if(m_pName)-    {-        VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);-        m_pName = VMA_NULL;-    }-}-#endif // _VMA_ALLOCATION_T_FUNCTIONS--#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS-VmaBlockVector::VmaBlockVector(-    VmaAllocator hAllocator,-    VmaPool hParentPool,-    uint32_t memoryTypeIndex,-    VkDeviceSize preferredBlockSize,-    size_t minBlockCount,-    size_t maxBlockCount,-    VkDeviceSize bufferImageGranularity,-    bool explicitBlockSize,-    uint32_t algorithm,-    float priority,-    VkDeviceSize minAllocationAlignment,-    void* pMemoryAllocateNext)-    : m_hAllocator(hAllocator),-    m_hParentPool(hParentPool),-    m_MemoryTypeIndex(memoryTypeIndex),-    m_PreferredBlockSize(preferredBlockSize),-    m_MinBlockCount(minBlockCount),-    m_MaxBlockCount(maxBlockCount),-    m_BufferImageGranularity(bufferImageGranularity),-    m_ExplicitBlockSize(explicitBlockSize),-    m_Algorithm(algorithm),-    m_Priority(priority),-    m_MinAllocationAlignment(minAllocationAlignment),-    m_pMemoryAllocateNext(pMemoryAllocateNext),-    m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),-    m_NextBlockId(0) {}--VmaBlockVector::~VmaBlockVector()-{-    for (size_t i = m_Blocks.size(); i--; )-    {-        m_Blocks[i]->Destroy(m_hAllocator);-        vma_delete(m_hAllocator, m_Blocks[i]);-    }-}--VkResult VmaBlockVector::CreateMinBlocks()-{-    for (size_t i = 0; i < m_MinBlockCount; ++i)-    {-        VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);-        if (res != VK_SUCCESS)-        {-            return res;-        }-    }-    return VK_SUCCESS;-}--void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)-{-    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);--    const size_t blockCount = m_Blocks.size();-    for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)-    {-        const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];-        VMA_ASSERT(pBlock);-        VMA_HEAVY_ASSERT(pBlock->Validate());-        pBlock->m_pMetadata->AddStatistics(inoutStats);-    }-}--void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)-{-    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);--    const size_t blockCount = m_Blocks.size();-    for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)-    {-        const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];-        VMA_ASSERT(pBlock);-        VMA_HEAVY_ASSERT(pBlock->Validate());-        pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);-    }-}--bool VmaBlockVector::IsEmpty()-{-    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);-    return m_Blocks.empty();-}--bool VmaBlockVector::IsCorruptionDetectionEnabled() const-{-    const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;-    return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&-        (VMA_DEBUG_MARGIN > 0) &&-        (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&-        (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;-}--VkResult VmaBlockVector::Allocate(-    VkDeviceSize size,-    VkDeviceSize alignment,-    const VmaAllocationCreateInfo& createInfo,-    VmaSuballocationType suballocType,-    size_t allocationCount,-    VmaAllocation* pAllocations)-{-    size_t allocIndex;-    VkResult res = VK_SUCCESS;--    alignment = VMA_MAX(alignment, m_MinAllocationAlignment);--    if (IsCorruptionDetectionEnabled())-    {-        size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));-        alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));-    }--    {-        VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);-        for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)-        {-            res = AllocatePage(-                size,-                alignment,-                createInfo,-                suballocType,-                pAllocations + allocIndex);-            if (res != VK_SUCCESS)-            {-                break;-            }-        }-    }--    if (res != VK_SUCCESS)-    {-        // Free all already created allocations.-        while (allocIndex--)-            Free(pAllocations[allocIndex]);-        memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);-    }--    return res;-}--VkResult VmaBlockVector::AllocatePage(-    VkDeviceSize size,-    VkDeviceSize alignment,-    const VmaAllocationCreateInfo& createInfo,-    VmaSuballocationType suballocType,-    VmaAllocation* pAllocation)-{-    const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;--    VkDeviceSize freeMemory;-    {-        const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);-        VmaBudget heapBudget = {};-        m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);-        freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;-    }--    const bool canFallbackToDedicated = !HasExplicitBlockSize() &&-        (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;-    const bool canCreateNewBlock =-        ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&-        (m_Blocks.size() < m_MaxBlockCount) &&-        (freeMemory >= size || !canFallbackToDedicated);-    uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;--    // Upper address can only be used with linear allocator and within single memory block.-    if (isUpperAddress &&-        (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))-    {-        return VK_ERROR_FEATURE_NOT_PRESENT;-    }--    // Early reject: requested allocation size is larger that maximum block size for this block vector.-    if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)-    {-        return VK_ERROR_OUT_OF_DEVICE_MEMORY;-    }--    // 1. Search existing allocations. Try to allocate.-    if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)-    {-        // Use only last block.-        if (!m_Blocks.empty())-        {-            VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();-            VMA_ASSERT(pCurrBlock);-            VkResult res = AllocateFromBlock(-                pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);-            if (res == VK_SUCCESS)-            {-                VMA_DEBUG_LOG_FORMAT("    Returned from last block #%u", pCurrBlock->GetId());-                IncrementallySortBlocks();-                return VK_SUCCESS;-            }-        }-    }-    else-    {-        if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default-        {-            const bool isHostVisible =-                (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;-            if(isHostVisible)-            {-                const bool isMappingAllowed = (createInfo.flags &-                    (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;-                /*-                For non-mappable allocations, check blocks that are not mapped first.-                For mappable allocations, check blocks that are already mapped first.-                This way, having many blocks, we will separate mappable and non-mappable allocations,-                hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc.-                */-                for(size_t mappingI = 0; mappingI < 2; ++mappingI)-                {-                    // Forward order in m_Blocks - prefer blocks with smallest amount of free space.-                    for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)-                    {-                        VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];-                        VMA_ASSERT(pCurrBlock);-                        const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;-                        if((mappingI == 0) == (isMappingAllowed == isBlockMapped))-                        {-                            VkResult res = AllocateFromBlock(-                                pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);-                            if (res == VK_SUCCESS)-                            {-                                VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%u", pCurrBlock->GetId());-                                IncrementallySortBlocks();-                                return VK_SUCCESS;-                            }-                        }-                    }-                }-            }-            else-            {-                // Forward order in m_Blocks - prefer blocks with smallest amount of free space.-                for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)-                {-                    VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];-                    VMA_ASSERT(pCurrBlock);-                    VkResult res = AllocateFromBlock(-                        pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);-                    if (res == VK_SUCCESS)-                    {-                        VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%u", pCurrBlock->GetId());-                        IncrementallySortBlocks();-                        return VK_SUCCESS;-                    }-                }-            }-        }-        else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT-        {-            // Backward order in m_Blocks - prefer blocks with largest amount of free space.-            for (size_t blockIndex = m_Blocks.size(); blockIndex--; )-            {-                VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];-                VMA_ASSERT(pCurrBlock);-                VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);-                if (res == VK_SUCCESS)-                {-                    VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%u", pCurrBlock->GetId());-                    IncrementallySortBlocks();-                    return VK_SUCCESS;-                }-            }-        }-    }--    // 2. Try to create new block.-    if (canCreateNewBlock)-    {-        // Calculate optimal size for new block.-        VkDeviceSize newBlockSize = m_PreferredBlockSize;-        uint32_t newBlockSizeShift = 0;-        const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;--        if (!m_ExplicitBlockSize)-        {-            // Allocate 1/8, 1/4, 1/2 as first blocks.-            const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();-            for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)-            {-                const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;-                if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)-                {-                    newBlockSize = smallerNewBlockSize;-                    ++newBlockSizeShift;-                }-                else-                {-                    break;-                }-            }-        }--        size_t newBlockIndex = 0;-        VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?-            CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;-        // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.-        if (!m_ExplicitBlockSize)-        {-            while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)-            {-                const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;-                if (smallerNewBlockSize >= size)-                {-                    newBlockSize = smallerNewBlockSize;-                    ++newBlockSizeShift;-                    res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?-                        CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;-                }-                else-                {-                    break;-                }-            }-        }--        if (res == VK_SUCCESS)-        {-            VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];-            VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);--            res = AllocateFromBlock(-                pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);-            if (res == VK_SUCCESS)-            {-                VMA_DEBUG_LOG_FORMAT("    Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize);-                IncrementallySortBlocks();-                return VK_SUCCESS;-            }-            else-            {-                // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.-                return VK_ERROR_OUT_OF_DEVICE_MEMORY;-            }-        }-    }--    return VK_ERROR_OUT_OF_DEVICE_MEMORY;-}--void VmaBlockVector::Free(const VmaAllocation hAllocation)-{-    VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;--    bool budgetExceeded = false;-    {-        const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);-        VmaBudget heapBudget = {};-        m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);-        budgetExceeded = heapBudget.usage >= heapBudget.budget;-    }--    // Scope for lock.-    {-        VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);--        VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();--        if (IsCorruptionDetectionEnabled())-        {-            VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());-            VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");-        }--        if (hAllocation->IsPersistentMap())-        {-            pBlock->Unmap(m_hAllocator, 1);-        }--        const bool hadEmptyBlockBeforeFree = HasEmptyBlock();-        pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());-        pBlock->PostFree(m_hAllocator);-        VMA_HEAVY_ASSERT(pBlock->Validate());--        VMA_DEBUG_LOG_FORMAT("  Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex);--        const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;-        // pBlock became empty after this deallocation.-        if (pBlock->m_pMetadata->IsEmpty())-        {-            // Already had empty block. We don't want to have two, so delete this one.-            if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)-            {-                pBlockToDelete = pBlock;-                Remove(pBlock);-            }-            // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth.-        }-        // pBlock didn't become empty, but we have another empty block - find and free that one.-        // (This is optional, heuristics.)-        else if (hadEmptyBlockBeforeFree && canDeleteBlock)-        {-            VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();-            if (pLastBlock->m_pMetadata->IsEmpty())-            {-                pBlockToDelete = pLastBlock;-                m_Blocks.pop_back();-            }-        }--        IncrementallySortBlocks();-    }--    // Destruction of a free block. Deferred until this point, outside of mutex-    // lock, for performance reason.-    if (pBlockToDelete != VMA_NULL)-    {-        VMA_DEBUG_LOG_FORMAT("    Deleted empty block #%u", pBlockToDelete->GetId());-        pBlockToDelete->Destroy(m_hAllocator);-        vma_delete(m_hAllocator, pBlockToDelete);-    }--    m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());-    m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);-}--VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const-{-    VkDeviceSize result = 0;-    for (size_t i = m_Blocks.size(); i--; )-    {-        result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());-        if (result >= m_PreferredBlockSize)-        {-            break;-        }-    }-    return result;-}--void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)-{-    for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)-    {-        if (m_Blocks[blockIndex] == pBlock)-        {-            VmaVectorRemove(m_Blocks, blockIndex);-            return;-        }-    }-    VMA_ASSERT(0);-}--void VmaBlockVector::IncrementallySortBlocks()-{-    if (!m_IncrementalSort)-        return;-    if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)-    {-        // Bubble sort only until first swap.-        for (size_t i = 1; i < m_Blocks.size(); ++i)-        {-            if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())-            {-                VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);-                return;-            }-        }-    }-}--void VmaBlockVector::SortByFreeSize()-{-    VMA_SORT(m_Blocks.begin(), m_Blocks.end(),-        [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool-        {-            return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();-        });-}--VkResult VmaBlockVector::AllocateFromBlock(-    VmaDeviceMemoryBlock* pBlock,-    VkDeviceSize size,-    VkDeviceSize alignment,-    VmaAllocationCreateFlags allocFlags,-    void* pUserData,-    VmaSuballocationType suballocType,-    uint32_t strategy,-    VmaAllocation* pAllocation)-{-    const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;--    VmaAllocationRequest currRequest = {};-    if (pBlock->m_pMetadata->CreateAllocationRequest(-        size,-        alignment,-        isUpperAddress,-        suballocType,-        strategy,-        &currRequest))-    {-        return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);-    }-    return VK_ERROR_OUT_OF_DEVICE_MEMORY;-}--VkResult VmaBlockVector::CommitAllocationRequest(-    VmaAllocationRequest& allocRequest,-    VmaDeviceMemoryBlock* pBlock,-    VkDeviceSize alignment,-    VmaAllocationCreateFlags allocFlags,-    void* pUserData,-    VmaSuballocationType suballocType,-    VmaAllocation* pAllocation)-{-    const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;-    const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;-    const bool isMappingAllowed = (allocFlags &-        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;--    pBlock->PostAlloc(m_hAllocator);-    // Allocate from pCurrBlock.-    if (mapped)-    {-        VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);-        if (res != VK_SUCCESS)-        {-            return res;-        }-    }--    *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);-    pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);-    (*pAllocation)->InitBlockAllocation(-        pBlock,-        allocRequest.allocHandle,-        alignment,-        allocRequest.size, // Not size, as actual allocation size may be larger than requested!-        m_MemoryTypeIndex,-        suballocType,-        mapped);-    VMA_HEAVY_ASSERT(pBlock->Validate());-    if (isUserDataString)-        (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);-    else-        (*pAllocation)->SetUserData(m_hAllocator, pUserData);-    m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);-    if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)-    {-        m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);-    }-    if (IsCorruptionDetectionEnabled())-    {-        VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);-        VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");-    }-    return VK_SUCCESS;-}--VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)-{-    VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };-    allocInfo.pNext = m_pMemoryAllocateNext;-    allocInfo.memoryTypeIndex = m_MemoryTypeIndex;-    allocInfo.allocationSize = blockSize;--#if VMA_BUFFER_DEVICE_ADDRESS-    // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.-    VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };-    if (m_hAllocator->m_UseKhrBufferDeviceAddress)-    {-        allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;-        VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);-    }-#endif // VMA_BUFFER_DEVICE_ADDRESS--#if VMA_MEMORY_PRIORITY-    VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };-    if (m_hAllocator->m_UseExtMemoryPriority)-    {-        VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f);-        priorityInfo.priority = m_Priority;-        VmaPnextChainPushFront(&allocInfo, &priorityInfo);-    }-#endif // VMA_MEMORY_PRIORITY--#if VMA_EXTERNAL_MEMORY-    // Attach VkExportMemoryAllocateInfoKHR if necessary.-    VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };-    exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);-    if (exportMemoryAllocInfo.handleTypes != 0)-    {-        VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);-    }-#endif // VMA_EXTERNAL_MEMORY--    VkDeviceMemory mem = VK_NULL_HANDLE;-    VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);-    if (res < 0)-    {-        return res;-    }--    // New VkDeviceMemory successfully created.--    // Create new Allocation for it.-    VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);-    pBlock->Init(-        m_hAllocator,-        m_hParentPool,-        m_MemoryTypeIndex,-        mem,-        allocInfo.allocationSize,-        m_NextBlockId++,-        m_Algorithm,-        m_BufferImageGranularity);--    m_Blocks.push_back(pBlock);-    if (pNewBlockIndex != VMA_NULL)-    {-        *pNewBlockIndex = m_Blocks.size() - 1;-    }--    return VK_SUCCESS;-}--bool VmaBlockVector::HasEmptyBlock()-{-    for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)-    {-        VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];-        if (pBlock->m_pMetadata->IsEmpty())-        {-            return true;-        }-    }-    return false;-}--#if VMA_STATS_STRING_ENABLED-void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)-{-    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);---    json.BeginObject();-    for (size_t i = 0; i < m_Blocks.size(); ++i)-    {-        json.BeginString();-        json.ContinueString(m_Blocks[i]->GetId());-        json.EndString();--        json.BeginObject();-        json.WriteString("MapRefCount");-        json.WriteNumber(m_Blocks[i]->GetMapRefCount());--        m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);-        json.EndObject();-    }-    json.EndObject();-}-#endif // VMA_STATS_STRING_ENABLED--VkResult VmaBlockVector::CheckCorruption()-{-    if (!IsCorruptionDetectionEnabled())-    {-        return VK_ERROR_FEATURE_NOT_PRESENT;-    }--    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);-    for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)-    {-        VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];-        VMA_ASSERT(pBlock);-        VkResult res = pBlock->CheckCorruption(m_hAllocator);-        if (res != VK_SUCCESS)-        {-            return res;-        }-    }-    return VK_SUCCESS;-}--#endif // _VMA_BLOCK_VECTOR_FUNCTIONS--#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS-VmaDefragmentationContext_T::VmaDefragmentationContext_T(-    VmaAllocator hAllocator,-    const VmaDefragmentationInfo& info)-    : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),-    m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),-    m_BreakCallback(info.pfnBreakCallback),-    m_BreakCallbackUserData(info.pBreakCallbackUserData),-    m_MoveAllocator(hAllocator->GetAllocationCallbacks()),-    m_Moves(m_MoveAllocator)-{-    m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK;--    if (info.pool != VMA_NULL)-    {-        m_BlockVectorCount = 1;-        m_PoolBlockVector = &info.pool->m_BlockVector;-        m_pBlockVectors = &m_PoolBlockVector;-        m_PoolBlockVector->SetIncrementalSort(false);-        m_PoolBlockVector->SortByFreeSize();-    }-    else-    {-        m_BlockVectorCount = hAllocator->GetMemoryTypeCount();-        m_PoolBlockVector = VMA_NULL;-        m_pBlockVectors = hAllocator->m_pBlockVectors;-        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)-        {-            VmaBlockVector* vector = m_pBlockVectors[i];-            if (vector != VMA_NULL)-            {-                vector->SetIncrementalSort(false);-                vector->SortByFreeSize();-            }-        }-    }--    switch (m_Algorithm)-    {-    case 0: // Default algorithm-        m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;-        m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);-        break;-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:-        m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);-        break;-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:-        if (hAllocator->GetBufferImageGranularity() > 1)-        {-            m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);-        }-        break;-    }-}--VmaDefragmentationContext_T::~VmaDefragmentationContext_T()-{-    if (m_PoolBlockVector != VMA_NULL)-    {-        m_PoolBlockVector->SetIncrementalSort(true);-    }-    else-    {-        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)-        {-            VmaBlockVector* vector = m_pBlockVectors[i];-            if (vector != VMA_NULL)-                vector->SetIncrementalSort(true);-        }-    }--    if (m_AlgorithmState)-    {-        switch (m_Algorithm)-        {-        case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:-            vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);-            break;-        case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:-            vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);-            break;-        default:-            VMA_ASSERT(0);-        }-    }-}--VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)-{-    if (m_PoolBlockVector != VMA_NULL)-    {-        VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);--        if (m_PoolBlockVector->GetBlockCount() > 1)-            ComputeDefragmentation(*m_PoolBlockVector, 0);-        else if (m_PoolBlockVector->GetBlockCount() == 1)-            ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));-    }-    else-    {-        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)-        {-            if (m_pBlockVectors[i] != VMA_NULL)-            {-                VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);--                if (m_pBlockVectors[i]->GetBlockCount() > 1)-                {-                    if (ComputeDefragmentation(*m_pBlockVectors[i], i))-                        break;-                }-                else if (m_pBlockVectors[i]->GetBlockCount() == 1)-                {-                    if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))-                        break;-                }-            }-        }-    }--    moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());-    if (moveInfo.moveCount > 0)-    {-        moveInfo.pMoves = m_Moves.data();-        return VK_INCOMPLETE;-    }--    moveInfo.pMoves = VMA_NULL;-    return VK_SUCCESS;-}--VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)-{-    VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);--    VkResult result = VK_SUCCESS;-    VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);-    VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);-    VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);--    VmaAllocator allocator = VMA_NULL;-    for (uint32_t i = 0; i < moveInfo.moveCount; ++i)-    {-        VmaDefragmentationMove& move = moveInfo.pMoves[i];-        size_t prevCount = 0, currentCount = 0;-        VkDeviceSize freedBlockSize = 0;--        uint32_t vectorIndex;-        VmaBlockVector* vector;-        if (m_PoolBlockVector != VMA_NULL)-        {-            vectorIndex = 0;-            vector = m_PoolBlockVector;-        }-        else-        {-            vectorIndex = move.srcAllocation->GetMemoryTypeIndex();-            vector = m_pBlockVectors[vectorIndex];-            VMA_ASSERT(vector != VMA_NULL);-        }--        switch (move.operation)-        {-        case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:-        {-            uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);-            if (mapCount > 0)-            {-                allocator = vector->m_hAllocator;-                VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();-                bool notPresent = true;-                for (FragmentedBlock& block : mappedBlocks)-                {-                    if (block.block == newMapBlock)-                    {-                        notPresent = false;-                        block.data += mapCount;-                        break;-                    }-                }-                if (notPresent)-                    mappedBlocks.push_back({ mapCount, newMapBlock });-            }--            // Scope for locks, Free have it's own lock-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                prevCount = vector->GetBlockCount();-                freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();-            }-            vector->Free(move.dstTmpAllocation);-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                currentCount = vector->GetBlockCount();-            }--            result = VK_INCOMPLETE;-            break;-        }-        case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:-        {-            m_PassStats.bytesMoved -= move.srcAllocation->GetSize();-            --m_PassStats.allocationsMoved;-            vector->Free(move.dstTmpAllocation);--            VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();-            bool notPresent = true;-            for (const FragmentedBlock& block : immovableBlocks)-            {-                if (block.block == newBlock)-                {-                    notPresent = false;-                    break;-                }-            }-            if (notPresent)-                immovableBlocks.push_back({ vectorIndex, newBlock });-            break;-        }-        case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:-        {-            m_PassStats.bytesMoved -= move.srcAllocation->GetSize();-            --m_PassStats.allocationsMoved;-            // Scope for locks, Free have it's own lock-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                prevCount = vector->GetBlockCount();-                freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();-            }-            vector->Free(move.srcAllocation);-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                currentCount = vector->GetBlockCount();-            }-            freedBlockSize *= prevCount - currentCount;--            VkDeviceSize dstBlockSize;-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();-            }-            vector->Free(move.dstTmpAllocation);-            {-                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);-                freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());-                currentCount = vector->GetBlockCount();-            }--            result = VK_INCOMPLETE;-            break;-        }-        default:-            VMA_ASSERT(0);-        }--        if (prevCount > currentCount)-        {-            size_t freedBlocks = prevCount - currentCount;-            m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);-            m_PassStats.bytesFreed += freedBlockSize;-        }--        if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT &&-            m_AlgorithmState != VMA_NULL)-        {-            // Avoid unnecessary tries to allocate when new free block is available-            StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];-            if (state.firstFreeBlock != SIZE_MAX)-            {-                const size_t diff = prevCount - currentCount;-                if (state.firstFreeBlock >= diff)-                {-                    state.firstFreeBlock -= diff;-                    if (state.firstFreeBlock != 0)-                        state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();-                }-                else-                    state.firstFreeBlock = 0;-            }-        }-    }-    moveInfo.moveCount = 0;-    moveInfo.pMoves = VMA_NULL;-    m_Moves.clear();--    // Update stats-    m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;-    m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;-    m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;-    m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;-    m_PassStats = { 0 };--    // Move blocks with immovable allocations according to algorithm-    if (immovableBlocks.size() > 0)-    {-        do-        {-            if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT)-            {-                if (m_AlgorithmState != VMA_NULL)-                {-                    bool swapped = false;-                    // Move to the start of free blocks range-                    for (const FragmentedBlock& block : immovableBlocks)-                    {-                        StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];-                        if (state.operation != StateExtensive::Operation::Cleanup)-                        {-                            VmaBlockVector* vector = m_pBlockVectors[block.data];-                            VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);--                            for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)-                            {-                                if (vector->GetBlock(i) == block.block)-                                {-                                    VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);-                                    if (state.firstFreeBlock != SIZE_MAX)-                                    {-                                        if (i + 1 < state.firstFreeBlock)-                                        {-                                            if (state.firstFreeBlock > 1)-                                                VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);-                                            else-                                                --state.firstFreeBlock;-                                        }-                                    }-                                    swapped = true;-                                    break;-                                }-                            }-                        }-                    }-                    if (swapped)-                        result = VK_INCOMPLETE;-                    break;-                }-            }--            // Move to the beginning-            for (const FragmentedBlock& block : immovableBlocks)-            {-                VmaBlockVector* vector = m_pBlockVectors[block.data];-                VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);--                for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)-                {-                    if (vector->GetBlock(i) == block.block)-                    {-                        VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);-                        break;-                    }-                }-            }-        } while (false);-    }--    // Bulk-map destination blocks-    for (const FragmentedBlock& block : mappedBlocks)-    {-        VkResult res = block.block->Map(allocator, block.data, VMA_NULL);-        VMA_ASSERT(res == VK_SUCCESS);-    }-    return result;-}--bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)-{-    switch (m_Algorithm)-    {-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:-        return ComputeDefragmentation_Fast(vector);-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:-        return ComputeDefragmentation_Balanced(vector, index, true);-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:-        return ComputeDefragmentation_Full(vector);-    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:-        return ComputeDefragmentation_Extensive(vector, index);-    default:-        VMA_ASSERT(0);-        return ComputeDefragmentation_Balanced(vector, index, true);-    }-}--VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(-    VmaAllocHandle handle, VmaBlockMetadata* metadata)-{-    MoveAllocationData moveData;-    moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);-    moveData.size = moveData.move.srcAllocation->GetSize();-    moveData.alignment = moveData.move.srcAllocation->GetAlignment();-    moveData.type = moveData.move.srcAllocation->GetSuballocationType();-    moveData.flags = 0;--    if (moveData.move.srcAllocation->IsPersistentMap())-        moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;-    if (moveData.move.srcAllocation->IsMappingAllowed())-        moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;--    return moveData;-}--VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)-{-    // Check custom criteria if exists-    if (m_BreakCallback && m_BreakCallback(m_BreakCallbackUserData))-        return CounterStatus::End;--    // Ignore allocation if will exceed max size for copy-    if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)-    {-        if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)-            return CounterStatus::Ignore;-        else-            return CounterStatus::End;-    }-    else-        m_IgnoredAllocs = 0;-    return CounterStatus::Pass;-}--bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)-{-    m_PassStats.bytesMoved += bytes;-    // Early return when max found-    if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)-    {-        VMA_ASSERT((m_PassStats.allocationsMoved == m_MaxPassAllocations ||-            m_PassStats.bytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");-        return true;-    }-    return false;-}--bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)-{-    VmaBlockMetadata* metadata = block->m_pMetadata;--    for (VmaAllocHandle handle = metadata->GetAllocationListBegin();-        handle != VK_NULL_HANDLE;-        handle = metadata->GetNextAllocation(handle))-    {-        MoveAllocationData moveData = GetMoveData(handle, metadata);-        // Ignore newly created allocations by defragmentation algorithm-        if (moveData.move.srcAllocation->GetUserData() == this)-            continue;-        switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-        {-        case CounterStatus::Ignore:-            continue;-        case CounterStatus::End:-            return true;-        case CounterStatus::Pass:-            break;-        default:-            VMA_ASSERT(0);-        }--        VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();-        if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)-        {-            VmaAllocationRequest request = {};-            if (metadata->CreateAllocationRequest(-                moveData.size,-                moveData.alignment,-                false,-                moveData.type,-                VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,-                &request))-            {-                if (metadata->GetAllocationOffset(request.allocHandle) < offset)-                {-                    if (vector.CommitAllocationRequest(-                        request,-                        block,-                        moveData.alignment,-                        moveData.flags,-                        this,-                        moveData.type,-                        &moveData.move.dstTmpAllocation) == VK_SUCCESS)-                    {-                        m_Moves.push_back(moveData.move);-                        if (IncrementCounters(moveData.size))-                            return true;-                    }-                }-            }-        }-    }-    return false;-}--bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)-{-    for (; start < end; ++start)-    {-        VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);-        if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)-        {-            if (vector.AllocateFromBlock(dstBlock,-                data.size,-                data.alignment,-                data.flags,-                this,-                data.type,-                0,-                &data.move.dstTmpAllocation) == VK_SUCCESS)-            {-                m_Moves.push_back(data.move);-                if (IncrementCounters(data.size))-                    return true;-                break;-            }-        }-    }-    return false;-}--bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)-{-    // Move only between blocks--    // Go through allocations in last blocks and try to fit them inside first ones-    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)-    {-        VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;--        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();-            handle != VK_NULL_HANDLE;-            handle = metadata->GetNextAllocation(handle))-        {-            MoveAllocationData moveData = GetMoveData(handle, metadata);-            // Ignore newly created allocations by defragmentation algorithm-            if (moveData.move.srcAllocation->GetUserData() == this)-                continue;-            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-            {-            case CounterStatus::Ignore:-                continue;-            case CounterStatus::End:-                return true;-            case CounterStatus::Pass:-                break;-            default:-                VMA_ASSERT(0);-            }--            // Check all previous blocks for free space-            if (AllocInOtherBlock(0, i, moveData, vector))-                return true;-        }-    }-    return false;-}--bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)-{-    // Go over every allocation and try to fit it in previous blocks at lowest offsets,-    // if not possible: realloc within single block to minimize offset (exclude offset == 0),-    // but only if there are noticeable gaps between them (some heuristic, ex. average size of allocation in block)-    VMA_ASSERT(m_AlgorithmState != VMA_NULL);--    StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];-    if (update && vectorState.avgAllocSize == UINT64_MAX)-        UpdateVectorStatistics(vector, vectorState);--    const size_t startMoveCount = m_Moves.size();-    VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;-    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)-    {-        VmaDeviceMemoryBlock* block = vector.GetBlock(i);-        VmaBlockMetadata* metadata = block->m_pMetadata;-        VkDeviceSize prevFreeRegionSize = 0;--        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();-            handle != VK_NULL_HANDLE;-            handle = metadata->GetNextAllocation(handle))-        {-            MoveAllocationData moveData = GetMoveData(handle, metadata);-            // Ignore newly created allocations by defragmentation algorithm-            if (moveData.move.srcAllocation->GetUserData() == this)-                continue;-            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-            {-            case CounterStatus::Ignore:-                continue;-            case CounterStatus::End:-                return true;-            case CounterStatus::Pass:-                break;-            default:-                VMA_ASSERT(0);-            }--            // Check all previous blocks for free space-            const size_t prevMoveCount = m_Moves.size();-            if (AllocInOtherBlock(0, i, moveData, vector))-                return true;--            VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);-            // If no room found then realloc within block for lower offset-            VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();-            if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)-            {-                // Check if realloc will make sense-                if (prevFreeRegionSize >= minimalFreeRegion ||-                    nextFreeRegionSize >= minimalFreeRegion ||-                    moveData.size <= vectorState.avgFreeSize ||-                    moveData.size <= vectorState.avgAllocSize)-                {-                    VmaAllocationRequest request = {};-                    if (metadata->CreateAllocationRequest(-                        moveData.size,-                        moveData.alignment,-                        false,-                        moveData.type,-                        VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,-                        &request))-                    {-                        if (metadata->GetAllocationOffset(request.allocHandle) < offset)-                        {-                            if (vector.CommitAllocationRequest(-                                request,-                                block,-                                moveData.alignment,-                                moveData.flags,-                                this,-                                moveData.type,-                                &moveData.move.dstTmpAllocation) == VK_SUCCESS)-                            {-                                m_Moves.push_back(moveData.move);-                                if (IncrementCounters(moveData.size))-                                    return true;-                            }-                        }-                    }-                }-            }-            prevFreeRegionSize = nextFreeRegionSize;-        }-    }--    // No moves performed, update statistics to current vector state-    if (startMoveCount == m_Moves.size() && !update)-    {-        vectorState.avgAllocSize = UINT64_MAX;-        return ComputeDefragmentation_Balanced(vector, index, false);-    }-    return false;-}--bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)-{-    // Go over every allocation and try to fit it in previous blocks at lowest offsets,-    // if not possible: realloc within single block to minimize offset (exclude offset == 0)--    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)-    {-        VmaDeviceMemoryBlock* block = vector.GetBlock(i);-        VmaBlockMetadata* metadata = block->m_pMetadata;--        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();-            handle != VK_NULL_HANDLE;-            handle = metadata->GetNextAllocation(handle))-        {-            MoveAllocationData moveData = GetMoveData(handle, metadata);-            // Ignore newly created allocations by defragmentation algorithm-            if (moveData.move.srcAllocation->GetUserData() == this)-                continue;-            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-            {-            case CounterStatus::Ignore:-                continue;-            case CounterStatus::End:-                return true;-            case CounterStatus::Pass:-                break;-            default:-                VMA_ASSERT(0);-            }--            // Check all previous blocks for free space-            const size_t prevMoveCount = m_Moves.size();-            if (AllocInOtherBlock(0, i, moveData, vector))-                return true;--            // If no room found then realloc within block for lower offset-            VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();-            if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)-            {-                VmaAllocationRequest request = {};-                if (metadata->CreateAllocationRequest(-                    moveData.size,-                    moveData.alignment,-                    false,-                    moveData.type,-                    VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,-                    &request))-                {-                    if (metadata->GetAllocationOffset(request.allocHandle) < offset)-                    {-                        if (vector.CommitAllocationRequest(-                            request,-                            block,-                            moveData.alignment,-                            moveData.flags,-                            this,-                            moveData.type,-                            &moveData.move.dstTmpAllocation) == VK_SUCCESS)-                        {-                            m_Moves.push_back(moveData.move);-                            if (IncrementCounters(moveData.size))-                                return true;-                        }-                    }-                }-            }-        }-    }-    return false;-}--bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)-{-    // First free single block, then populate it to the brim, then free another block, and so on--    // Fallback to previous algorithm since without granularity conflicts it can achieve max packing-    if (vector.m_BufferImageGranularity == 1)-        return ComputeDefragmentation_Full(vector);--    VMA_ASSERT(m_AlgorithmState != VMA_NULL);--    StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];--    bool texturePresent = false, bufferPresent = false, otherPresent = false;-    switch (vectorState.operation)-    {-    case StateExtensive::Operation::Done: // Vector defragmented-        return false;-    case StateExtensive::Operation::FindFreeBlockBuffer:-    case StateExtensive::Operation::FindFreeBlockTexture:-    case StateExtensive::Operation::FindFreeBlockAll:-    {-        // No more blocks to free, just perform fast realloc and move to cleanup-        if (vectorState.firstFreeBlock == 0)-        {-            vectorState.operation = StateExtensive::Operation::Cleanup;-            return ComputeDefragmentation_Fast(vector);-        }--        // No free blocks, have to clear last one-        size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;-        VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;--        const size_t prevMoveCount = m_Moves.size();-        for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();-            handle != VK_NULL_HANDLE;-            handle = freeMetadata->GetNextAllocation(handle))-        {-            MoveAllocationData moveData = GetMoveData(handle, freeMetadata);-            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-            {-            case CounterStatus::Ignore:-                continue;-            case CounterStatus::End:-                return true;-            case CounterStatus::Pass:-                break;-            default:-                VMA_ASSERT(0);-            }--            // Check all previous blocks for free space-            if (AllocInOtherBlock(0, last, moveData, vector))-            {-                // Full clear performed already-                if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)-                    vectorState.firstFreeBlock = last;-                return true;-            }-        }--        if (prevMoveCount == m_Moves.size())-        {-            // Cannot perform full clear, have to move data in other blocks around-            if (last != 0)-            {-                for (size_t i = last - 1; i; --i)-                {-                    if (ReallocWithinBlock(vector, vector.GetBlock(i)))-                        return true;-                }-            }--            if (prevMoveCount == m_Moves.size())-            {-                // No possible reallocs within blocks, try to move them around fast-                return ComputeDefragmentation_Fast(vector);-            }-        }-        else-        {-            switch (vectorState.operation)-            {-            case StateExtensive::Operation::FindFreeBlockBuffer:-                vectorState.operation = StateExtensive::Operation::MoveBuffers;-                break;-            case StateExtensive::Operation::FindFreeBlockTexture:-                vectorState.operation = StateExtensive::Operation::MoveTextures;-                break;-            case StateExtensive::Operation::FindFreeBlockAll:-                vectorState.operation = StateExtensive::Operation::MoveAll;-                break;-            default:-                VMA_ASSERT(0);-                vectorState.operation = StateExtensive::Operation::MoveTextures;-            }-            vectorState.firstFreeBlock = last;-            // Nothing done, block found without reallocations, can perform another reallocs in same pass-            return ComputeDefragmentation_Extensive(vector, index);-        }-        break;-    }-    case StateExtensive::Operation::MoveTextures:-    {-        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,-            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))-        {-            if (texturePresent)-            {-                vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;-                return ComputeDefragmentation_Extensive(vector, index);-            }--            if (!bufferPresent && !otherPresent)-            {-                vectorState.operation = StateExtensive::Operation::Cleanup;-                break;-            }--            // No more textures to move, check buffers-            vectorState.operation = StateExtensive::Operation::MoveBuffers;-            bufferPresent = false;-            otherPresent = false;-        }-        else-            break;-        VMA_FALLTHROUGH; // Fallthrough-    }-    case StateExtensive::Operation::MoveBuffers:-    {-        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,-            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))-        {-            if (bufferPresent)-            {-                vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;-                return ComputeDefragmentation_Extensive(vector, index);-            }--            if (!otherPresent)-            {-                vectorState.operation = StateExtensive::Operation::Cleanup;-                break;-            }--            // No more buffers to move, check all others-            vectorState.operation = StateExtensive::Operation::MoveAll;-            otherPresent = false;-        }-        else-            break;-        VMA_FALLTHROUGH; // Fallthrough-    }-    case StateExtensive::Operation::MoveAll:-    {-        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,-            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))-        {-            if (otherPresent)-            {-                vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;-                return ComputeDefragmentation_Extensive(vector, index);-            }-            // Everything moved-            vectorState.operation = StateExtensive::Operation::Cleanup;-        }-        break;-    }-    case StateExtensive::Operation::Cleanup:-        // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062).-        break;-    }--    if (vectorState.operation == StateExtensive::Operation::Cleanup)-    {-        // All other work done, pack data in blocks even tighter if possible-        const size_t prevMoveCount = m_Moves.size();-        for (size_t i = 0; i < vector.GetBlockCount(); ++i)-        {-            if (ReallocWithinBlock(vector, vector.GetBlock(i)))-                return true;-        }--        if (prevMoveCount == m_Moves.size())-            vectorState.operation = StateExtensive::Operation::Done;-    }-    return false;-}--void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)-{-    size_t allocCount = 0;-    size_t freeCount = 0;-    state.avgFreeSize = 0;-    state.avgAllocSize = 0;--    for (size_t i = 0; i < vector.GetBlockCount(); ++i)-    {-        VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;--        allocCount += metadata->GetAllocationCount();-        freeCount += metadata->GetFreeRegionsCount();-        state.avgFreeSize += metadata->GetSumFreeSize();-        state.avgAllocSize += metadata->GetSize();-    }--    state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;-    state.avgFreeSize /= freeCount;-}--bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,-    VmaBlockVector& vector, size_t firstFreeBlock,-    bool& texturePresent, bool& bufferPresent, bool& otherPresent)-{-    const size_t prevMoveCount = m_Moves.size();-    for (size_t i = firstFreeBlock ; i;)-    {-        VmaDeviceMemoryBlock* block = vector.GetBlock(--i);-        VmaBlockMetadata* metadata = block->m_pMetadata;--        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();-            handle != VK_NULL_HANDLE;-            handle = metadata->GetNextAllocation(handle))-        {-            MoveAllocationData moveData = GetMoveData(handle, metadata);-            // Ignore newly created allocations by defragmentation algorithm-            if (moveData.move.srcAllocation->GetUserData() == this)-                continue;-            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))-            {-            case CounterStatus::Ignore:-                continue;-            case CounterStatus::End:-                return true;-            case CounterStatus::Pass:-                break;-            default:-                VMA_ASSERT(0);-            }--            // Move only single type of resources at once-            if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))-            {-                // Try to fit allocation into free blocks-                if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))-                    return false;-            }--            if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))-                texturePresent = true;-            else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))-                bufferPresent = true;-            else-                otherPresent = true;-        }-    }-    return prevMoveCount == m_Moves.size();-}-#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS--#ifndef _VMA_POOL_T_FUNCTIONS-VmaPool_T::VmaPool_T(-    VmaAllocator hAllocator,-    const VmaPoolCreateInfo& createInfo,-    VkDeviceSize preferredBlockSize)-    : m_BlockVector(-        hAllocator,-        this, // hParentPool-        createInfo.memoryTypeIndex,-        createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,-        createInfo.minBlockCount,-        createInfo.maxBlockCount,-        (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),-        createInfo.blockSize != 0, // explicitBlockSize-        createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm-        createInfo.priority,-        VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),-        createInfo.pMemoryAllocateNext),-    m_Id(0),-    m_Name(VMA_NULL) {}--VmaPool_T::~VmaPool_T()-{-    VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);-}--void VmaPool_T::SetName(const char* pName)-{-    const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();-    VmaFreeString(allocs, m_Name);--    if (pName != VMA_NULL)-    {-        m_Name = VmaCreateStringCopy(allocs, pName);-    }-    else-    {-        m_Name = VMA_NULL;-    }-}-#endif // _VMA_POOL_T_FUNCTIONS--#ifndef _VMA_ALLOCATOR_T_FUNCTIONS-VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :-    m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),-    m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),-    m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),-    m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),-    m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),-    m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),-    m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),-    m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),-    m_hDevice(pCreateInfo->device),-    m_hInstance(pCreateInfo->instance),-    m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),-    m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?-        *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),-    m_AllocationObjectAllocator(&m_AllocationCallbacks),-    m_HeapSizeLimitMask(0),-    m_DeviceMemoryCount(0),-    m_PreferredLargeHeapBlockSize(0),-    m_PhysicalDevice(pCreateInfo->physicalDevice),-    m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),-    m_NextPoolId(0),-    m_GlobalMemoryTypeBits(UINT32_MAX)-{-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        m_UseKhrDedicatedAllocation = false;-        m_UseKhrBindMemory2 = false;-    }--    if(VMA_DEBUG_DETECT_CORRUPTION)-    {-        // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.-        VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);-    }--    VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);--    if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))-    {-#if !(VMA_DEDICATED_ALLOCATION)-        if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)-        {-            VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");-        }-#endif-#if !(VMA_BIND_MEMORY2)-        if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)-        {-            VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");-        }-#endif-    }-#if !(VMA_MEMORY_BUDGET)-    if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)-    {-        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");-    }-#endif-#if !(VMA_BUFFER_DEVICE_ADDRESS)-    if(m_UseKhrBufferDeviceAddress)-    {-        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");-    }-#endif-#if VMA_VULKAN_VERSION < 1003000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))-    {-        VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros.");-    }-#endif-#if VMA_VULKAN_VERSION < 1002000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0))-    {-        VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");-    }-#endif-#if VMA_VULKAN_VERSION < 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");-    }-#endif-#if !(VMA_MEMORY_PRIORITY)-    if(m_UseExtMemoryPriority)-    {-        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");-    }-#endif--    memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));-    memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));-    memset(&m_MemProps, 0, sizeof(m_MemProps));--    memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));-    memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));--#if VMA_EXTERNAL_MEMORY-    memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));-#endif // #if VMA_EXTERNAL_MEMORY--    if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)-    {-        m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;-        m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;-        m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;-    }--    ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);--    (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);-    (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);--    VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));-    VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));-    VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));-    VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));--    m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?-        pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);--    m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();--#if VMA_EXTERNAL_MEMORY-    if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)-    {-        memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,-            sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());-    }-#endif // #if VMA_EXTERNAL_MEMORY--    if(pCreateInfo->pHeapSizeLimit != VMA_NULL)-    {-        for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)-        {-            const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];-            if(limit != VK_WHOLE_SIZE)-            {-                m_HeapSizeLimitMask |= 1u << heapIndex;-                if(limit < m_MemProps.memoryHeaps[heapIndex].size)-                {-                    m_MemProps.memoryHeaps[heapIndex].size = limit;-                }-            }-        }-    }--    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-    {-        // Create only supported types-        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)-        {-            const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);-            m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(-                this,-                VK_NULL_HANDLE, // hParentPool-                memTypeIndex,-                preferredBlockSize,-                0,-                SIZE_MAX,-                GetBufferImageGranularity(),-                false, // explicitBlockSize-                0, // algorithm-                0.5f, // priority (0.5 is the default per Vulkan spec)-                GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment-                VMA_NULL); // // pMemoryAllocateNext-            // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,-            // becase minBlockCount is 0.-        }-    }-}--VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)-{-    VkResult res = VK_SUCCESS;--#if VMA_MEMORY_BUDGET-    if(m_UseExtMemoryBudget)-    {-        UpdateVulkanBudget();-    }-#endif // #if VMA_MEMORY_BUDGET--    return res;-}--VmaAllocator_T::~VmaAllocator_T()-{-    VMA_ASSERT(m_Pools.IsEmpty());--    for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )-    {-        vma_delete(this, m_pBlockVectors[memTypeIndex]);-    }-}--void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)-{-#if VMA_STATIC_VULKAN_FUNCTIONS == 1-    ImportVulkanFunctions_Static();-#endif--    if(pVulkanFunctions != VMA_NULL)-    {-        ImportVulkanFunctions_Custom(pVulkanFunctions);-    }--#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1-    ImportVulkanFunctions_Dynamic();-#endif--    ValidateVulkanFunctions();-}--#if VMA_STATIC_VULKAN_FUNCTIONS == 1--void VmaAllocator_T::ImportVulkanFunctions_Static()-{-    // Vulkan 1.0-    m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;-    m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;-    m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;-    m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;-    m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;-    m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;-    m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;-    m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;-    m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;-    m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;-    m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;-    m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;-    m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;-    m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;-    m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;-    m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;-    m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;-    m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;-    m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;--    // Vulkan 1.1-#if VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;-        m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;-        m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;-        m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;-    }-#endif--#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;-    }-#endif--#if VMA_VULKAN_VERSION >= 1003000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))-    {-        m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;-        m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;-    }-#endif-}--#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1--void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)-{-    VMA_ASSERT(pVulkanFunctions != VMA_NULL);--#define VMA_COPY_IF_NOT_NULL(funcName) \-    if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;--    VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);-    VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);-    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);-    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);-    VMA_COPY_IF_NOT_NULL(vkAllocateMemory);-    VMA_COPY_IF_NOT_NULL(vkFreeMemory);-    VMA_COPY_IF_NOT_NULL(vkMapMemory);-    VMA_COPY_IF_NOT_NULL(vkUnmapMemory);-    VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);-    VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);-    VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);-    VMA_COPY_IF_NOT_NULL(vkBindImageMemory);-    VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);-    VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);-    VMA_COPY_IF_NOT_NULL(vkCreateBuffer);-    VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);-    VMA_COPY_IF_NOT_NULL(vkCreateImage);-    VMA_COPY_IF_NOT_NULL(vkDestroyImage);-    VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);--#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);-    VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);-#endif--#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000-    VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);-    VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);-#endif--#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);-#endif--#if VMA_VULKAN_VERSION >= 1003000-    VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);-    VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);-#endif--#undef VMA_COPY_IF_NOT_NULL-}--#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1--void VmaAllocator_T::ImportVulkanFunctions_Dynamic()-{-    VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&-        "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "-        "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "-        "Other members can be null.");--#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \-    if(m_VulkanFunctions.memberName == VMA_NULL) \-        m_VulkanFunctions.memberName = \-            (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString);-#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \-    if(m_VulkanFunctions.memberName == VMA_NULL) \-        m_VulkanFunctions.memberName = \-            (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString);--    VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");-    VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");-    VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");-    VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");-    VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");-    VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");-    VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");-    VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");-    VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");-    VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");-    VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");-    VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");-    VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");-    VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");-    VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");-    VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");-    VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");--#if VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");-        VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");-        VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");-        VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");-    }-#endif--#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2");-    }-    else if(m_UseExtMemoryBudget)-    {-        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2KHR");-    }-#endif--#if VMA_DEDICATED_ALLOCATION-    if(m_UseKhrDedicatedAllocation)-    {-        VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");-        VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");-    }-#endif--#if VMA_BIND_MEMORY2-    if(m_UseKhrBindMemory2)-    {-        VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");-        VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");-    }-#endif // #if VMA_BIND_MEMORY2--#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");-    }-    else if(m_UseExtMemoryBudget)-    {-        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");-    }-#endif // #if VMA_MEMORY_BUDGET--#if VMA_VULKAN_VERSION >= 1003000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))-    {-        VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");-        VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");-    }-#endif--#undef VMA_FETCH_DEVICE_FUNC-#undef VMA_FETCH_INSTANCE_FUNC-}--#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1--void VmaAllocator_T::ValidateVulkanFunctions()-{-    VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);-    VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);--#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)-    {-        VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);-        VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);-    }-#endif--#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)-    {-        VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);-        VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);-    }-#endif--#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000-    if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);-    }-#endif--#if VMA_VULKAN_VERSION >= 1003000-    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))-    {-        VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);-        VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);-    }-#endif-}--VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)-{-    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);-    const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;-    const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;-    return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);-}--VkResult VmaAllocator_T::AllocateMemoryOfType(-    VmaPool pool,-    VkDeviceSize size,-    VkDeviceSize alignment,-    bool dedicatedPreferred,-    VkBuffer dedicatedBuffer,-    VkImage dedicatedImage,-    VkFlags dedicatedBufferImageUsage,-    const VmaAllocationCreateInfo& createInfo,-    uint32_t memTypeIndex,-    VmaSuballocationType suballocType,-    VmaDedicatedAllocationList& dedicatedAllocations,-    VmaBlockVector& blockVector,-    size_t allocationCount,-    VmaAllocation* pAllocations)-{-    VMA_ASSERT(pAllocations != VMA_NULL);-    VMA_DEBUG_LOG_FORMAT("  AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size);--    VmaAllocationCreateInfo finalCreateInfo = createInfo;-    VkResult res = CalcMemTypeParams(-        finalCreateInfo,-        memTypeIndex,-        size,-        allocationCount);-    if(res != VK_SUCCESS)-        return res;--    if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)-    {-        return AllocateDedicatedMemory(-            pool,-            size,-            suballocType,-            dedicatedAllocations,-            memTypeIndex,-            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,-            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,-            (finalCreateInfo.flags &-                (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,-            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,-            finalCreateInfo.pUserData,-            finalCreateInfo.priority,-            dedicatedBuffer,-            dedicatedImage,-            dedicatedBufferImageUsage,-            allocationCount,-            pAllocations,-            blockVector.GetAllocationNextPtr());-    }-    else-    {-        const bool canAllocateDedicated =-            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&-            (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());--        if(canAllocateDedicated)-        {-            // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.-            if(size > blockVector.GetPreferredBlockSize() / 2)-            {-                dedicatedPreferred = true;-            }-            // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,-            // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above-            // 3/4 of the maximum allocation count.-            if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 &&-                m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)-            {-                dedicatedPreferred = false;-            }--            if(dedicatedPreferred)-            {-                res = AllocateDedicatedMemory(-                    pool,-                    size,-                    suballocType,-                    dedicatedAllocations,-                    memTypeIndex,-                    (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,-                    (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,-                    (finalCreateInfo.flags &-                        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,-                    (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,-                    finalCreateInfo.pUserData,-                    finalCreateInfo.priority,-                    dedicatedBuffer,-                    dedicatedImage,-                    dedicatedBufferImageUsage,-                    allocationCount,-                    pAllocations,-                    blockVector.GetAllocationNextPtr());-                if(res == VK_SUCCESS)-                {-                    // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.-                    VMA_DEBUG_LOG("    Allocated as DedicatedMemory");-                    return VK_SUCCESS;-                }-            }-        }--        res = blockVector.Allocate(-            size,-            alignment,-            finalCreateInfo,-            suballocType,-            allocationCount,-            pAllocations);-        if(res == VK_SUCCESS)-            return VK_SUCCESS;--        // Try dedicated memory.-        if(canAllocateDedicated && !dedicatedPreferred)-        {-            res = AllocateDedicatedMemory(-                pool,-                size,-                suballocType,-                dedicatedAllocations,-                memTypeIndex,-                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,-                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,-                (finalCreateInfo.flags &-                    (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,-                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,-                finalCreateInfo.pUserData,-                finalCreateInfo.priority,-                dedicatedBuffer,-                dedicatedImage,-                dedicatedBufferImageUsage,-                allocationCount,-                pAllocations,-                blockVector.GetAllocationNextPtr());-            if(res == VK_SUCCESS)-            {-                // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.-                VMA_DEBUG_LOG("    Allocated as DedicatedMemory");-                return VK_SUCCESS;-            }-        }-        // Everything failed: Return error code.-        VMA_DEBUG_LOG("    vkAllocateMemory FAILED");-        return res;-    }-}--VkResult VmaAllocator_T::AllocateDedicatedMemory(-    VmaPool pool,-    VkDeviceSize size,-    VmaSuballocationType suballocType,-    VmaDedicatedAllocationList& dedicatedAllocations,-    uint32_t memTypeIndex,-    bool map,-    bool isUserDataString,-    bool isMappingAllowed,-    bool canAliasMemory,-    void* pUserData,-    float priority,-    VkBuffer dedicatedBuffer,-    VkImage dedicatedImage,-    VkFlags dedicatedBufferImageUsage,-    size_t allocationCount,-    VmaAllocation* pAllocations,-    const void* pNextChain)-{-    VMA_ASSERT(allocationCount > 0 && pAllocations);--    VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };-    allocInfo.memoryTypeIndex = memTypeIndex;-    allocInfo.allocationSize = size;-    allocInfo.pNext = pNextChain;--#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };-    if(!canAliasMemory)-    {-        if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-        {-            if(dedicatedBuffer != VK_NULL_HANDLE)-            {-                VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);-                dedicatedAllocInfo.buffer = dedicatedBuffer;-                VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);-            }-            else if(dedicatedImage != VK_NULL_HANDLE)-            {-                dedicatedAllocInfo.image = dedicatedImage;-                VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);-            }-        }-    }-#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000--#if VMA_BUFFER_DEVICE_ADDRESS-    VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };-    if(m_UseKhrBufferDeviceAddress)-    {-        bool canContainBufferWithDeviceAddress = true;-        if(dedicatedBuffer != VK_NULL_HANDLE)-        {-            canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX || // Usage flags unknown-                (dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0;-        }-        else if(dedicatedImage != VK_NULL_HANDLE)-        {-            canContainBufferWithDeviceAddress = false;-        }-        if(canContainBufferWithDeviceAddress)-        {-            allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;-            VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);-        }-    }-#endif // #if VMA_BUFFER_DEVICE_ADDRESS--#if VMA_MEMORY_PRIORITY-    VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };-    if(m_UseExtMemoryPriority)-    {-        VMA_ASSERT(priority >= 0.f && priority <= 1.f);-        priorityInfo.priority = priority;-        VmaPnextChainPushFront(&allocInfo, &priorityInfo);-    }-#endif // #if VMA_MEMORY_PRIORITY--#if VMA_EXTERNAL_MEMORY-    // Attach VkExportMemoryAllocateInfoKHR if necessary.-    VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };-    exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);-    if(exportMemoryAllocInfo.handleTypes != 0)-    {-        VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);-    }-#endif // #if VMA_EXTERNAL_MEMORY--    size_t allocIndex;-    VkResult res = VK_SUCCESS;-    for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex)-    {-        res = AllocateDedicatedMemoryPage(-            pool,-            size,-            suballocType,-            memTypeIndex,-            allocInfo,-            map,-            isUserDataString,-            isMappingAllowed,-            pUserData,-            pAllocations + allocIndex);-        if(res != VK_SUCCESS)-        {-            break;-        }-    }--    if(res == VK_SUCCESS)-    {-        for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)-        {-            dedicatedAllocations.Register(pAllocations[allocIndex]);-        }-        VMA_DEBUG_LOG_FORMAT("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex);-    }-    else-    {-        // Free all already created allocations.-        while(allocIndex--)-        {-            VmaAllocation currAlloc = pAllocations[allocIndex];-            VkDeviceMemory hMemory = currAlloc->GetMemory();--            /*-            There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory-            before vkFreeMemory.--            if(currAlloc->GetMappedData() != VMA_NULL)-            {-                (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);-            }-            */--            FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);-            m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());-            m_AllocationObjectAllocator.Free(currAlloc);-        }--        memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);-    }--    return res;-}--VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(-    VmaPool pool,-    VkDeviceSize size,-    VmaSuballocationType suballocType,-    uint32_t memTypeIndex,-    const VkMemoryAllocateInfo& allocInfo,-    bool map,-    bool isUserDataString,-    bool isMappingAllowed,-    void* pUserData,-    VmaAllocation* pAllocation)-{-    VkDeviceMemory hMemory = VK_NULL_HANDLE;-    VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);-    if(res < 0)-    {-        VMA_DEBUG_LOG("    vkAllocateMemory FAILED");-        return res;-    }--    void* pMappedData = VMA_NULL;-    if(map)-    {-        res = (*m_VulkanFunctions.vkMapMemory)(-            m_hDevice,-            hMemory,-            0,-            VK_WHOLE_SIZE,-            0,-            &pMappedData);-        if(res < 0)-        {-            VMA_DEBUG_LOG("    vkMapMemory FAILED");-            FreeVulkanMemory(memTypeIndex, size, hMemory);-            return res;-        }-    }--    *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);-    (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size);-    if (isUserDataString)-        (*pAllocation)->SetName(this, (const char*)pUserData);-    else-        (*pAllocation)->SetUserData(this, pUserData);-    m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);-    if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)-    {-        FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);-    }--    return VK_SUCCESS;-}--void VmaAllocator_T::GetBufferMemoryRequirements(-    VkBuffer hBuffer,-    VkMemoryRequirements& memReq,-    bool& requiresDedicatedAllocation,-    bool& prefersDedicatedAllocation) const-{-#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };-        memReqInfo.buffer = hBuffer;--        VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };--        VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };-        VmaPnextChainPushFront(&memReq2, &memDedicatedReq);--        (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);--        memReq = memReq2.memoryRequirements;-        requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);-        prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);-    }-    else-#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    {-        (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);-        requiresDedicatedAllocation = false;-        prefersDedicatedAllocation  = false;-    }-}--void VmaAllocator_T::GetImageMemoryRequirements(-    VkImage hImage,-    VkMemoryRequirements& memReq,-    bool& requiresDedicatedAllocation,-    bool& prefersDedicatedAllocation) const-{-#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))-    {-        VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };-        memReqInfo.image = hImage;--        VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };--        VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };-        VmaPnextChainPushFront(&memReq2, &memDedicatedReq);--        (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);--        memReq = memReq2.memoryRequirements;-        requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);-        prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);-    }-    else-#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000-    {-        (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);-        requiresDedicatedAllocation = false;-        prefersDedicatedAllocation  = false;-    }-}--VkResult VmaAllocator_T::FindMemoryTypeIndex(-    uint32_t memoryTypeBits,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    VkFlags bufImgUsage,-    uint32_t* pMemoryTypeIndex) const-{-    memoryTypeBits &= GetGlobalMemoryTypeBits();--    if(pAllocationCreateInfo->memoryTypeBits != 0)-    {-        memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;-    }--    VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0;-    if(!FindMemoryPreferences(-        IsIntegratedGpu(),-        *pAllocationCreateInfo,-        bufImgUsage,-        requiredFlags, preferredFlags, notPreferredFlags))-    {-        return VK_ERROR_FEATURE_NOT_PRESENT;-    }--    *pMemoryTypeIndex = UINT32_MAX;-    uint32_t minCost = UINT32_MAX;-    for(uint32_t memTypeIndex = 0, memTypeBit = 1;-        memTypeIndex < GetMemoryTypeCount();-        ++memTypeIndex, memTypeBit <<= 1)-    {-        // This memory type is acceptable according to memoryTypeBits bitmask.-        if((memTypeBit & memoryTypeBits) != 0)-        {-            const VkMemoryPropertyFlags currFlags =-                m_MemProps.memoryTypes[memTypeIndex].propertyFlags;-            // This memory type contains requiredFlags.-            if((requiredFlags & ~currFlags) == 0)-            {-                // Calculate cost as number of bits from preferredFlags not present in this memory type.-                uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) +-                    VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);-                // Remember memory type with lowest cost.-                if(currCost < minCost)-                {-                    *pMemoryTypeIndex = memTypeIndex;-                    if(currCost == 0)-                    {-                        return VK_SUCCESS;-                    }-                    minCost = currCost;-                }-            }-        }-    }-    return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;-}--VkResult VmaAllocator_T::CalcMemTypeParams(-    VmaAllocationCreateInfo& inoutCreateInfo,-    uint32_t memTypeIndex,-    VkDeviceSize size,-    size_t allocationCount)-{-    // If memory type is not HOST_VISIBLE, disable MAPPED.-    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&-        (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)-    {-        inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;-    }--    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&-        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)-    {-        const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);-        VmaBudget heapBudget = {};-        GetHeapBudgets(&heapBudget, heapIndex, 1);-        if(heapBudget.usage + size * allocationCount > heapBudget.budget)-        {-            return VK_ERROR_OUT_OF_DEVICE_MEMORY;-        }-    }-    return VK_SUCCESS;-}--VkResult VmaAllocator_T::CalcAllocationParams(-    VmaAllocationCreateInfo& inoutCreateInfo,-    bool dedicatedRequired,-    bool dedicatedPreferred)-{-    VMA_ASSERT((inoutCreateInfo.flags &-        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=-        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&-        "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");-    VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||-        (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&-        "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");-    if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)-    {-        if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)-        {-            VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&-                "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");-        }-    }--    // If memory is lazily allocated, it should be always dedicated.-    if(dedicatedRequired ||-        inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)-    {-        inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;-    }--    if(inoutCreateInfo.pool != VK_NULL_HANDLE)-    {-        if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&-            (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)-        {-            VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");-            return VK_ERROR_FEATURE_NOT_PRESENT;-        }-        inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();-    }--    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&-        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)-    {-        VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");-        return VK_ERROR_FEATURE_NOT_PRESENT;-    }--    if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&-        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)-    {-        inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;-    }--    // Non-auto USAGE values imply HOST_ACCESS flags.-    // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools.-    // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*.-    // Otherwise they just protect from assert on mapping.-    if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&-        inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&-        inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)-    {-        if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)-        {-            inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;-        }-    }--    return VK_SUCCESS;-}--VkResult VmaAllocator_T::AllocateMemory(-    const VkMemoryRequirements& vkMemReq,-    bool requiresDedicatedAllocation,-    bool prefersDedicatedAllocation,-    VkBuffer dedicatedBuffer,-    VkImage dedicatedImage,-    VkFlags dedicatedBufferImageUsage,-    const VmaAllocationCreateInfo& createInfo,-    VmaSuballocationType suballocType,-    size_t allocationCount,-    VmaAllocation* pAllocations)-{-    memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);--    VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));--    if(vkMemReq.size == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VmaAllocationCreateInfo createInfoFinal = createInfo;-    VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation);-    if(res != VK_SUCCESS)-        return res;--    if(createInfoFinal.pool != VK_NULL_HANDLE)-    {-        VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;-        return AllocateMemoryOfType(-            createInfoFinal.pool,-            vkMemReq.size,-            vkMemReq.alignment,-            prefersDedicatedAllocation,-            dedicatedBuffer,-            dedicatedImage,-            dedicatedBufferImageUsage,-            createInfoFinal,-            blockVector.GetMemoryTypeIndex(),-            suballocType,-            createInfoFinal.pool->m_DedicatedAllocations,-            blockVector,-            allocationCount,-            pAllocations);-    }-    else-    {-        // Bit mask of memory Vulkan types acceptable for this allocation.-        uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;-        uint32_t memTypeIndex = UINT32_MAX;-        res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);-        // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.-        if(res != VK_SUCCESS)-            return res;-        do-        {-            VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];-            VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");-            res = AllocateMemoryOfType(-                VK_NULL_HANDLE,-                vkMemReq.size,-                vkMemReq.alignment,-                requiresDedicatedAllocation || prefersDedicatedAllocation,-                dedicatedBuffer,-                dedicatedImage,-                dedicatedBufferImageUsage,-                createInfoFinal,-                memTypeIndex,-                suballocType,-                m_DedicatedAllocations[memTypeIndex],-                *blockVector,-                allocationCount,-                pAllocations);-            // Allocation succeeded-            if(res == VK_SUCCESS)-                return VK_SUCCESS;--            // Remove old memTypeIndex from list of possibilities.-            memoryTypeBits &= ~(1u << memTypeIndex);-            // Find alternative memTypeIndex.-            res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);-        } while(res == VK_SUCCESS);--        // No other matching memory type index could be found.-        // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.-        return VK_ERROR_OUT_OF_DEVICE_MEMORY;-    }-}--void VmaAllocator_T::FreeMemory(-    size_t allocationCount,-    const VmaAllocation* pAllocations)-{-    VMA_ASSERT(pAllocations);--    for(size_t allocIndex = allocationCount; allocIndex--; )-    {-        VmaAllocation allocation = pAllocations[allocIndex];--        if(allocation != VK_NULL_HANDLE)-        {-            if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)-            {-                FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);-            }--            allocation->FreeName(this);--            switch(allocation->GetType())-            {-            case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-                {-                    VmaBlockVector* pBlockVector = VMA_NULL;-                    VmaPool hPool = allocation->GetParentPool();-                    if(hPool != VK_NULL_HANDLE)-                    {-                        pBlockVector = &hPool->m_BlockVector;-                    }-                    else-                    {-                        const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();-                        pBlockVector = m_pBlockVectors[memTypeIndex];-                        VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");-                    }-                    pBlockVector->Free(allocation);-                }-                break;-            case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-                FreeDedicatedMemory(allocation);-                break;-            default:-                VMA_ASSERT(0);-            }-        }-    }-}--void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)-{-    // Initialize.-    VmaClearDetailedStatistics(pStats->total);-    for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)-        VmaClearDetailedStatistics(pStats->memoryType[i]);-    for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)-        VmaClearDetailedStatistics(pStats->memoryHeap[i]);--    // Process default pools.-    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-    {-        VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];-        if (pBlockVector != VMA_NULL)-            pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);-    }--    // Process custom pools.-    {-        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);-        for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))-        {-            VmaBlockVector& blockVector = pool->m_BlockVector;-            const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();-            blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);-            pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);-        }-    }--    // Process dedicated allocations.-    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-    {-        m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);-    }--    // Sum from memory types to memory heaps.-    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-    {-        const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;-        VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);-    }--    // Sum from memory heaps to total.-    for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)-        VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);--    VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||-        pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);-    VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||-        pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);-}--void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)-{-#if VMA_MEMORY_BUDGET-    if(m_UseExtMemoryBudget)-    {-        if(m_Budget.m_OperationsSinceBudgetFetch < 30)-        {-            VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);-            for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)-            {-                const uint32_t heapIndex = firstHeap + i;--                outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];-                outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];-                outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];-                outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];--                if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])-                {-                    outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] +-                        outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];-                }-                else-                {-                    outBudgets->usage = 0;-                }--                // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.-                outBudgets->budget = VMA_MIN(-                    m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);-            }-        }-        else-        {-            UpdateVulkanBudget(); // Outside of mutex lock-            GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion-        }-    }-    else-#endif-    {-        for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)-        {-            const uint32_t heapIndex = firstHeap + i;--            outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];-            outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];-            outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];-            outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];--            outBudgets->usage = outBudgets->statistics.blockBytes;-            outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.-        }-    }-}--void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)-{-    pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();-    pAllocationInfo->deviceMemory = hAllocation->GetMemory();-    pAllocationInfo->offset = hAllocation->GetOffset();-    pAllocationInfo->size = hAllocation->GetSize();-    pAllocationInfo->pMappedData = hAllocation->GetMappedData();-    pAllocationInfo->pUserData = hAllocation->GetUserData();-    pAllocationInfo->pName = hAllocation->GetName();-}--VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)-{-    VMA_DEBUG_LOG_FORMAT("  CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags);--    VmaPoolCreateInfo newCreateInfo = *pCreateInfo;--    // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.-    if(pCreateInfo->pMemoryAllocateNext)-    {-        VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);-    }--    if(newCreateInfo.maxBlockCount == 0)-    {-        newCreateInfo.maxBlockCount = SIZE_MAX;-    }-    if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }-    // Memory type index out of range or forbidden.-    if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||-        ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)-    {-        return VK_ERROR_FEATURE_NOT_PRESENT;-    }-    if(newCreateInfo.minAllocationAlignment > 0)-    {-        VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));-    }--    const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);--    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);--    VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();-    if(res != VK_SUCCESS)-    {-        vma_delete(this, *pPool);-        *pPool = VMA_NULL;-        return res;-    }--    // Add to m_Pools.-    {-        VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);-        (*pPool)->SetId(m_NextPoolId++);-        m_Pools.PushBack(*pPool);-    }--    return VK_SUCCESS;-}--void VmaAllocator_T::DestroyPool(VmaPool pool)-{-    // Remove from m_Pools.-    {-        VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);-        m_Pools.Remove(pool);-    }--    vma_delete(this, pool);-}--void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)-{-    VmaClearStatistics(*pPoolStats);-    pool->m_BlockVector.AddStatistics(*pPoolStats);-    pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);-}--void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)-{-    VmaClearDetailedStatistics(*pPoolStats);-    pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);-    pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);-}--void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)-{-    m_CurrentFrameIndex.store(frameIndex);--#if VMA_MEMORY_BUDGET-    if(m_UseExtMemoryBudget)-    {-        UpdateVulkanBudget();-    }-#endif // #if VMA_MEMORY_BUDGET-}--VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)-{-    return hPool->m_BlockVector.CheckCorruption();-}--VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)-{-    VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;--    // Process default pools.-    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-    {-        VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];-        if(pBlockVector != VMA_NULL)-        {-            VkResult localRes = pBlockVector->CheckCorruption();-            switch(localRes)-            {-            case VK_ERROR_FEATURE_NOT_PRESENT:-                break;-            case VK_SUCCESS:-                finalRes = VK_SUCCESS;-                break;-            default:-                return localRes;-            }-        }-    }--    // Process custom pools.-    {-        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);-        for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))-        {-            if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)-            {-                VkResult localRes = pool->m_BlockVector.CheckCorruption();-                switch(localRes)-                {-                case VK_ERROR_FEATURE_NOT_PRESENT:-                    break;-                case VK_SUCCESS:-                    finalRes = VK_SUCCESS;-                    break;-                default:-                    return localRes;-                }-            }-        }-    }--    return finalRes;-}--VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)-{-    AtomicTransactionalIncrement<VMA_ATOMIC_UINT32> deviceMemoryCountIncrement;-    const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);-#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT-    if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)-    {-        return VK_ERROR_TOO_MANY_OBJECTS;-    }-#endif--    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);--    // HeapSizeLimit is in effect for this heap.-    if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0)-    {-        const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;-        VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];-        for(;;)-        {-            const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;-            if(blockBytesAfterAllocation > heapSize)-            {-                return VK_ERROR_OUT_OF_DEVICE_MEMORY;-            }-            if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))-            {-                break;-            }-        }-    }-    else-    {-        m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;-    }-    ++m_Budget.m_BlockCount[heapIndex];--    // VULKAN CALL vkAllocateMemory.-    VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);--    if(res == VK_SUCCESS)-    {-#if VMA_MEMORY_BUDGET-        ++m_Budget.m_OperationsSinceBudgetFetch;-#endif--        // Informative callback.-        if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)-        {-            (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);-        }--        deviceMemoryCountIncrement.Commit();-    }-    else-    {-        --m_Budget.m_BlockCount[heapIndex];-        m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;-    }--    return res;-}--void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)-{-    // Informative callback.-    if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)-    {-        (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);-    }--    // VULKAN CALL vkFreeMemory.-    (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());--    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);-    --m_Budget.m_BlockCount[heapIndex];-    m_Budget.m_BlockBytes[heapIndex] -= size;--    --m_DeviceMemoryCount;-}--VkResult VmaAllocator_T::BindVulkanBuffer(-    VkDeviceMemory memory,-    VkDeviceSize memoryOffset,-    VkBuffer buffer,-    const void* pNext)-{-    if(pNext != VMA_NULL)-    {-#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2-        if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&-            m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)-        {-            VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };-            bindBufferMemoryInfo.pNext = pNext;-            bindBufferMemoryInfo.buffer = buffer;-            bindBufferMemoryInfo.memory = memory;-            bindBufferMemoryInfo.memoryOffset = memoryOffset;-            return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);-        }-        else-#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2-        {-            return VK_ERROR_EXTENSION_NOT_PRESENT;-        }-    }-    else-    {-        return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);-    }-}--VkResult VmaAllocator_T::BindVulkanImage(-    VkDeviceMemory memory,-    VkDeviceSize memoryOffset,-    VkImage image,-    const void* pNext)-{-    if(pNext != VMA_NULL)-    {-#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2-        if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&-            m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)-        {-            VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };-            bindBufferMemoryInfo.pNext = pNext;-            bindBufferMemoryInfo.image = image;-            bindBufferMemoryInfo.memory = memory;-            bindBufferMemoryInfo.memoryOffset = memoryOffset;-            return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);-        }-        else-#endif // #if VMA_BIND_MEMORY2-        {-            return VK_ERROR_EXTENSION_NOT_PRESENT;-        }-    }-    else-    {-        return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);-    }-}--VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)-{-    switch(hAllocation->GetType())-    {-    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-        {-            VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();-            char *pBytes = VMA_NULL;-            VkResult res = pBlock->Map(this, 1, (void**)&pBytes);-            if(res == VK_SUCCESS)-            {-                *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();-                hAllocation->BlockAllocMap();-            }-            return res;-        }-        VMA_FALLTHROUGH; // Fallthrough-    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-        return hAllocation->DedicatedAllocMap(this, ppData);-    default:-        VMA_ASSERT(0);-        return VK_ERROR_MEMORY_MAP_FAILED;-    }-}--void VmaAllocator_T::Unmap(VmaAllocation hAllocation)-{-    switch(hAllocation->GetType())-    {-    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-        {-            VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();-            hAllocation->BlockAllocUnmap();-            pBlock->Unmap(this, 1);-        }-        break;-    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-        hAllocation->DedicatedAllocUnmap(this);-        break;-    default:-        VMA_ASSERT(0);-    }-}--VkResult VmaAllocator_T::BindBufferMemory(-    VmaAllocation hAllocation,-    VkDeviceSize allocationLocalOffset,-    VkBuffer hBuffer,-    const void* pNext)-{-    VkResult res = VK_ERROR_UNKNOWN;-    switch(hAllocation->GetType())-    {-    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-        res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);-        break;-    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-    {-        VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();-        VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");-        res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);-        break;-    }-    default:-        VMA_ASSERT(0);-    }-    return res;-}--VkResult VmaAllocator_T::BindImageMemory(-    VmaAllocation hAllocation,-    VkDeviceSize allocationLocalOffset,-    VkImage hImage,-    const void* pNext)-{-    VkResult res = VK_ERROR_UNKNOWN;-    switch(hAllocation->GetType())-    {-    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-        res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);-        break;-    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-    {-        VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();-        VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");-        res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);-        break;-    }-    default:-        VMA_ASSERT(0);-    }-    return res;-}--VkResult VmaAllocator_T::FlushOrInvalidateAllocation(-    VmaAllocation hAllocation,-    VkDeviceSize offset, VkDeviceSize size,-    VMA_CACHE_OPERATION op)-{-    VkResult res = VK_SUCCESS;--    VkMappedMemoryRange memRange = {};-    if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))-    {-        switch(op)-        {-        case VMA_CACHE_FLUSH:-            res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);-            break;-        case VMA_CACHE_INVALIDATE:-            res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);-            break;-        default:-            VMA_ASSERT(0);-        }-    }-    // else: Just ignore this call.-    return res;-}--VkResult VmaAllocator_T::FlushOrInvalidateAllocations(-    uint32_t allocationCount,-    const VmaAllocation* allocations,-    const VkDeviceSize* offsets, const VkDeviceSize* sizes,-    VMA_CACHE_OPERATION op)-{-    typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;-    typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;-    RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));--    for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)-    {-        const VmaAllocation alloc = allocations[allocIndex];-        const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;-        const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;-        VkMappedMemoryRange newRange;-        if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))-        {-            ranges.push_back(newRange);-        }-    }--    VkResult res = VK_SUCCESS;-    if(!ranges.empty())-    {-        switch(op)-        {-        case VMA_CACHE_FLUSH:-            res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());-            break;-        case VMA_CACHE_INVALIDATE:-            res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());-            break;-        default:-            VMA_ASSERT(0);-        }-    }-    // else: Just ignore this call.-    return res;-}--void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)-{-    VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);--    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();-    VmaPool parentPool = allocation->GetParentPool();-    if(parentPool == VK_NULL_HANDLE)-    {-        // Default pool-        m_DedicatedAllocations[memTypeIndex].Unregister(allocation);-    }-    else-    {-        // Custom pool-        parentPool->m_DedicatedAllocations.Unregister(allocation);-    }--    VkDeviceMemory hMemory = allocation->GetMemory();--    /*-    There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory-    before vkFreeMemory.--    if(allocation->GetMappedData() != VMA_NULL)-    {-        (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);-    }-    */--    FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);--    m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());-    m_AllocationObjectAllocator.Free(allocation);--    VMA_DEBUG_LOG_FORMAT("    Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex);-}--uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const-{-    VkBufferCreateInfo dummyBufCreateInfo;-    VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);--    uint32_t memoryTypeBits = 0;--    // Create buffer.-    VkBuffer buf = VK_NULL_HANDLE;-    VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(-        m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);-    if(res == VK_SUCCESS)-    {-        // Query for supported memory types.-        VkMemoryRequirements memReq;-        (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);-        memoryTypeBits = memReq.memoryTypeBits;--        // Destroy buffer.-        (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());-    }--    return memoryTypeBits;-}--uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const-{-    // Make sure memory information is already fetched.-    VMA_ASSERT(GetMemoryTypeCount() > 0);--    uint32_t memoryTypeBits = UINT32_MAX;--    if(!m_UseAmdDeviceCoherentMemory)-    {-        // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.-        for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-        {-            if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)-            {-                memoryTypeBits &= ~(1u << memTypeIndex);-            }-        }-    }--    return memoryTypeBits;-}--bool VmaAllocator_T::GetFlushOrInvalidateRange(-    VmaAllocation allocation,-    VkDeviceSize offset, VkDeviceSize size,-    VkMappedMemoryRange& outRange) const-{-    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();-    if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))-    {-        const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;-        const VkDeviceSize allocationSize = allocation->GetSize();-        VMA_ASSERT(offset <= allocationSize);--        outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;-        outRange.pNext = VMA_NULL;-        outRange.memory = allocation->GetMemory();--        switch(allocation->GetType())-        {-        case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:-            outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);-            if(size == VK_WHOLE_SIZE)-            {-                outRange.size = allocationSize - outRange.offset;-            }-            else-            {-                VMA_ASSERT(offset + size <= allocationSize);-                outRange.size = VMA_MIN(-                    VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),-                    allocationSize - outRange.offset);-            }-            break;-        case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:-        {-            // 1. Still within this allocation.-            outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);-            if(size == VK_WHOLE_SIZE)-            {-                size = allocationSize - offset;-            }-            else-            {-                VMA_ASSERT(offset + size <= allocationSize);-            }-            outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);--            // 2. Adjust to whole block.-            const VkDeviceSize allocationOffset = allocation->GetOffset();-            VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);-            const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();-            outRange.offset += allocationOffset;-            outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);--            break;-        }-        default:-            VMA_ASSERT(0);-        }-        return true;-    }-    return false;-}--#if VMA_MEMORY_BUDGET-void VmaAllocator_T::UpdateVulkanBudget()-{-    VMA_ASSERT(m_UseExtMemoryBudget);--    VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };--    VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };-    VmaPnextChainPushFront(&memProps, &budgetProps);--    GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);--    {-        VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);--        for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)-        {-            m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];-            m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];-            m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();--            // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.-            if(m_Budget.m_VulkanBudget[heapIndex] == 0)-            {-                m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.-            }-            else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)-            {-                m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;-            }-            if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)-            {-                m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];-            }-        }-        m_Budget.m_OperationsSinceBudgetFetch = 0;-    }-}-#endif // VMA_MEMORY_BUDGET--void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern)-{-    if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&-        hAllocation->IsMappingAllowed() &&-        (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)-    {-        void* pData = VMA_NULL;-        VkResult res = Map(hAllocation, &pData);-        if(res == VK_SUCCESS)-        {-            memset(pData, (int)pattern, (size_t)hAllocation->GetSize());-            FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);-            Unmap(hAllocation);-        }-        else-        {-            VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");-        }-    }-}--uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()-{-    uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();-    if(memoryTypeBits == UINT32_MAX)-    {-        memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();-        m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);-    }-    return memoryTypeBits;-}--#if VMA_STATS_STRING_ENABLED-void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)-{-    json.WriteString("DefaultPools");-    json.BeginObject();-    {-        for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-        {-            VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];-            VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];-            if (pBlockVector != VMA_NULL)-            {-                json.BeginString("Type ");-                json.ContinueString(memTypeIndex);-                json.EndString();-                json.BeginObject();-                {-                    json.WriteString("PreferredBlockSize");-                    json.WriteNumber(pBlockVector->GetPreferredBlockSize());--                    json.WriteString("Blocks");-                    pBlockVector->PrintDetailedMap(json);--                    json.WriteString("DedicatedAllocations");-                    dedicatedAllocList.BuildStatsString(json);-                }-                json.EndObject();-            }-        }-    }-    json.EndObject();--    json.WriteString("CustomPools");-    json.BeginObject();-    {-        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);-        if (!m_Pools.IsEmpty())-        {-            for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)-            {-                bool displayType = true;-                size_t index = 0;-                for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))-                {-                    VmaBlockVector& blockVector = pool->m_BlockVector;-                    if (blockVector.GetMemoryTypeIndex() == memTypeIndex)-                    {-                        if (displayType)-                        {-                            json.BeginString("Type ");-                            json.ContinueString(memTypeIndex);-                            json.EndString();-                            json.BeginArray();-                            displayType = false;-                        }--                        json.BeginObject();-                        {-                            json.WriteString("Name");-                            json.BeginString();-                            json.ContinueString((uint64_t)index++);-                            if (pool->GetName())-                            {-                                json.ContinueString(" - ");-                                json.ContinueString(pool->GetName());-                            }-                            json.EndString();--                            json.WriteString("PreferredBlockSize");-                            json.WriteNumber(blockVector.GetPreferredBlockSize());--                            json.WriteString("Blocks");-                            blockVector.PrintDetailedMap(json);--                            json.WriteString("DedicatedAllocations");-                            pool->m_DedicatedAllocations.BuildStatsString(json);-                        }-                        json.EndObject();-                    }-                }--                if (!displayType)-                    json.EndArray();-            }-        }-    }-    json.EndObject();-}-#endif // VMA_STATS_STRING_ENABLED-#endif // _VMA_ALLOCATOR_T_FUNCTIONS---#ifndef _VMA_PUBLIC_INTERFACE-VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(-    const VmaAllocatorCreateInfo* pCreateInfo,-    VmaAllocator* pAllocator)-{-    VMA_ASSERT(pCreateInfo && pAllocator);-    VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||-        (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3));-    VMA_DEBUG_LOG("vmaCreateAllocator");-    *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);-    VkResult result = (*pAllocator)->Init(pCreateInfo);-    if(result < 0)-    {-        vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);-        *pAllocator = VK_NULL_HANDLE;-    }-    return result;-}--VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(-    VmaAllocator allocator)-{-    if(allocator != VK_NULL_HANDLE)-    {-        VMA_DEBUG_LOG("vmaDestroyAllocator");-        VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.-        vma_delete(&allocationCallbacks, allocator);-    }-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)-{-    VMA_ASSERT(allocator && pAllocatorInfo);-    pAllocatorInfo->instance = allocator->m_hInstance;-    pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();-    pAllocatorInfo->device = allocator->m_hDevice;-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(-    VmaAllocator allocator,-    const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)-{-    VMA_ASSERT(allocator && ppPhysicalDeviceProperties);-    *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(-    VmaAllocator allocator,-    const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)-{-    VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);-    *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(-    VmaAllocator allocator,-    uint32_t memoryTypeIndex,-    VkMemoryPropertyFlags* pFlags)-{-    VMA_ASSERT(allocator && pFlags);-    VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());-    *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;-}--VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(-    VmaAllocator allocator,-    uint32_t frameIndex)-{-    VMA_ASSERT(allocator);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->SetCurrentFrameIndex(frameIndex);-}--VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(-    VmaAllocator allocator,-    VmaTotalStatistics* pStats)-{-    VMA_ASSERT(allocator && pStats);-    VMA_DEBUG_GLOBAL_MUTEX_LOCK-    allocator->CalculateStatistics(pStats);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(-    VmaAllocator allocator,-    VmaBudget* pBudgets)-{-    VMA_ASSERT(allocator && pBudgets);-    VMA_DEBUG_GLOBAL_MUTEX_LOCK-    allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());-}--#if VMA_STATS_STRING_ENABLED--VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(-    VmaAllocator allocator,-    char** ppStatsString,-    VkBool32 detailedMap)-{-    VMA_ASSERT(allocator && ppStatsString);-    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VmaStringBuilder sb(allocator->GetAllocationCallbacks());-    {-        VmaBudget budgets[VK_MAX_MEMORY_HEAPS];-        allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());--        VmaTotalStatistics stats;-        allocator->CalculateStatistics(&stats);--        VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);-        json.BeginObject();-        {-            json.WriteString("General");-            json.BeginObject();-            {-                const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;-                const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;--                json.WriteString("API");-                json.WriteString("Vulkan");--                json.WriteString("apiVersion");-                json.BeginString();-                json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion));-                json.ContinueString(".");-                json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion));-                json.ContinueString(".");-                json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion));-                json.EndString();--                json.WriteString("GPU");-                json.WriteString(deviceProperties.deviceName);-                json.WriteString("deviceType");-                json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));--                json.WriteString("maxMemoryAllocationCount");-                json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);-                json.WriteString("bufferImageGranularity");-                json.WriteNumber(deviceProperties.limits.bufferImageGranularity);-                json.WriteString("nonCoherentAtomSize");-                json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);--                json.WriteString("memoryHeapCount");-                json.WriteNumber(memoryProperties.memoryHeapCount);-                json.WriteString("memoryTypeCount");-                json.WriteNumber(memoryProperties.memoryTypeCount);-            }-            json.EndObject();-        }-        {-            json.WriteString("Total");-            VmaPrintDetailedStatistics(json, stats.total);-        }-        {-            json.WriteString("MemoryInfo");-            json.BeginObject();-            {-                for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)-                {-                    json.BeginString("Heap ");-                    json.ContinueString(heapIndex);-                    json.EndString();-                    json.BeginObject();-                    {-                        const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];-                        json.WriteString("Flags");-                        json.BeginArray(true);-                        {-                            if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)-                                json.WriteString("DEVICE_LOCAL");-                        #if VMA_VULKAN_VERSION >= 1001000-                            if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)-                                json.WriteString("MULTI_INSTANCE");-                        #endif--                            VkMemoryHeapFlags flags = heapInfo.flags &-                                ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT-                        #if VMA_VULKAN_VERSION >= 1001000-                                    | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT-                        #endif-                                    );-                            if (flags != 0)-                                json.WriteNumber(flags);-                        }-                        json.EndArray();--                        json.WriteString("Size");-                        json.WriteNumber(heapInfo.size);--                        json.WriteString("Budget");-                        json.BeginObject();-                        {-                            json.WriteString("BudgetBytes");-                            json.WriteNumber(budgets[heapIndex].budget);-                            json.WriteString("UsageBytes");-                            json.WriteNumber(budgets[heapIndex].usage);-                        }-                        json.EndObject();--                        json.WriteString("Stats");-                        VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);--                        json.WriteString("MemoryPools");-                        json.BeginObject();-                        {-                            for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)-                            {-                                if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)-                                {-                                    json.BeginString("Type ");-                                    json.ContinueString(typeIndex);-                                    json.EndString();-                                    json.BeginObject();-                                    {-                                        json.WriteString("Flags");-                                        json.BeginArray(true);-                                        {-                                            VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;-                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)-                                                json.WriteString("DEVICE_LOCAL");-                                            if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)-                                                json.WriteString("HOST_VISIBLE");-                                            if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)-                                                json.WriteString("HOST_COHERENT");-                                            if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)-                                                json.WriteString("HOST_CACHED");-                                            if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)-                                                json.WriteString("LAZILY_ALLOCATED");-                                        #if VMA_VULKAN_VERSION >= 1001000-                                            if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)-                                                json.WriteString("PROTECTED");-                                        #endif-                                        #if VK_AMD_device_coherent_memory-                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)-                                                json.WriteString("DEVICE_COHERENT_AMD");-                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)-                                                json.WriteString("DEVICE_UNCACHED_AMD");-                                        #endif--                                            flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT-                                        #if VMA_VULKAN_VERSION >= 1001000-                                                | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT-                                        #endif-                                        #if VK_AMD_device_coherent_memory-                                                | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY-                                                | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY-                                        #endif-                                                | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT-                                                | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT-                                                | VK_MEMORY_PROPERTY_HOST_CACHED_BIT);-                                            if (flags != 0)-                                                json.WriteNumber(flags);-                                        }-                                        json.EndArray();--                                        json.WriteString("Stats");-                                        VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);-                                    }-                                    json.EndObject();-                                }-                            }--                        }-                        json.EndObject();-                    }-                    json.EndObject();-                }-            }-            json.EndObject();-        }--        if (detailedMap == VK_TRUE)-            allocator->PrintDetailedMap(json);--        json.EndObject();-    }--    *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());-}--VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(-    VmaAllocator allocator,-    char* pStatsString)-{-    if(pStatsString != VMA_NULL)-    {-        VMA_ASSERT(allocator);-        VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);-    }-}--#endif // VMA_STATS_STRING_ENABLED--/*-This function is not protected by any mutex because it just reads immutable data.-*/-VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(-    VmaAllocator allocator,-    uint32_t memoryTypeBits,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    uint32_t* pMemoryTypeIndex)-{-    VMA_ASSERT(allocator != VK_NULL_HANDLE);-    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);-    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);--    return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(-    VmaAllocator allocator,-    const VkBufferCreateInfo* pBufferCreateInfo,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    uint32_t* pMemoryTypeIndex)-{-    VMA_ASSERT(allocator != VK_NULL_HANDLE);-    VMA_ASSERT(pBufferCreateInfo != VMA_NULL);-    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);-    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);--    const VkDevice hDev = allocator->m_hDevice;-    const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();-    VkResult res;--#if VMA_VULKAN_VERSION >= 1003000-    if(funcs->vkGetDeviceBufferMemoryRequirements)-    {-        // Can query straight from VkBufferCreateInfo :)-        VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS};-        devBufMemReq.pCreateInfo = pBufferCreateInfo;--        VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};-        (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);--        res = allocator->FindMemoryTypeIndex(-            memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);-    }-    else-#endif // #if VMA_VULKAN_VERSION >= 1003000-    {-        // Must create a dummy buffer to query :(-        VkBuffer hBuffer = VK_NULL_HANDLE;-        res = funcs->vkCreateBuffer(-            hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);-        if(res == VK_SUCCESS)-        {-            VkMemoryRequirements memReq = {};-            funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);--            res = allocator->FindMemoryTypeIndex(-                memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex);--            funcs->vkDestroyBuffer(-                hDev, hBuffer, allocator->GetAllocationCallbacks());-        }-    }-    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(-    VmaAllocator allocator,-    const VkImageCreateInfo* pImageCreateInfo,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    uint32_t* pMemoryTypeIndex)-{-    VMA_ASSERT(allocator != VK_NULL_HANDLE);-    VMA_ASSERT(pImageCreateInfo != VMA_NULL);-    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);-    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);--    const VkDevice hDev = allocator->m_hDevice;-    const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();-    VkResult res;--#if VMA_VULKAN_VERSION >= 1003000-    if(funcs->vkGetDeviceImageMemoryRequirements)-    {-        // Can query straight from VkImageCreateInfo :)-        VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS};-        devImgMemReq.pCreateInfo = pImageCreateInfo;-        VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&-            "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");--        VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};-        (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);--        res = allocator->FindMemoryTypeIndex(-            memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);-    }-    else-#endif // #if VMA_VULKAN_VERSION >= 1003000-    {-        // Must create a dummy image to query :(-        VkImage hImage = VK_NULL_HANDLE;-        res = funcs->vkCreateImage(-            hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);-        if(res == VK_SUCCESS)-        {-            VkMemoryRequirements memReq = {};-            funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);--            res = allocator->FindMemoryTypeIndex(-                memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex);--            funcs->vkDestroyImage(-                hDev, hImage, allocator->GetAllocationCallbacks());-        }-    }-    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(-    VmaAllocator allocator,-    const VmaPoolCreateInfo* pCreateInfo,-    VmaPool* pPool)-{-    VMA_ASSERT(allocator && pCreateInfo && pPool);--    VMA_DEBUG_LOG("vmaCreatePool");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->CreatePool(pCreateInfo, pPool);-}--VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(-    VmaAllocator allocator,-    VmaPool pool)-{-    VMA_ASSERT(allocator);--    if(pool == VK_NULL_HANDLE)-    {-        return;-    }--    VMA_DEBUG_LOG("vmaDestroyPool");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->DestroyPool(pool);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(-    VmaAllocator allocator,-    VmaPool pool,-    VmaStatistics* pPoolStats)-{-    VMA_ASSERT(allocator && pool && pPoolStats);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->GetPoolStatistics(pool, pPoolStats);-}--VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(-    VmaAllocator allocator,-    VmaPool pool,-    VmaDetailedStatistics* pPoolStats)-{-    VMA_ASSERT(allocator && pool && pPoolStats);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->CalculatePoolStatistics(pool, pPoolStats);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)-{-    VMA_ASSERT(allocator && pool);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VMA_DEBUG_LOG("vmaCheckPoolCorruption");--    return allocator->CheckPoolCorruption(pool);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(-    VmaAllocator allocator,-    VmaPool pool,-    const char** ppName)-{-    VMA_ASSERT(allocator && pool && ppName);--    VMA_DEBUG_LOG("vmaGetPoolName");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    *ppName = pool->GetName();-}--VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(-    VmaAllocator allocator,-    VmaPool pool,-    const char* pName)-{-    VMA_ASSERT(allocator && pool);--    VMA_DEBUG_LOG("vmaSetPoolName");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    pool->SetName(pName);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(-    VmaAllocator allocator,-    const VkMemoryRequirements* pVkMemoryRequirements,-    const VmaAllocationCreateInfo* pCreateInfo,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);--    VMA_DEBUG_LOG("vmaAllocateMemory");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VkResult result = allocator->AllocateMemory(-        *pVkMemoryRequirements,-        false, // requiresDedicatedAllocation-        false, // prefersDedicatedAllocation-        VK_NULL_HANDLE, // dedicatedBuffer-        VK_NULL_HANDLE, // dedicatedImage-        UINT32_MAX, // dedicatedBufferImageUsage-        *pCreateInfo,-        VMA_SUBALLOCATION_TYPE_UNKNOWN,-        1, // allocationCount-        pAllocation);--    if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)-    {-        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-    }--    return result;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(-    VmaAllocator allocator,-    const VkMemoryRequirements* pVkMemoryRequirements,-    const VmaAllocationCreateInfo* pCreateInfo,-    size_t allocationCount,-    VmaAllocation* pAllocations,-    VmaAllocationInfo* pAllocationInfo)-{-    if(allocationCount == 0)-    {-        return VK_SUCCESS;-    }--    VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);--    VMA_DEBUG_LOG("vmaAllocateMemoryPages");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VkResult result = allocator->AllocateMemory(-        *pVkMemoryRequirements,-        false, // requiresDedicatedAllocation-        false, // prefersDedicatedAllocation-        VK_NULL_HANDLE, // dedicatedBuffer-        VK_NULL_HANDLE, // dedicatedImage-        UINT32_MAX, // dedicatedBufferImageUsage-        *pCreateInfo,-        VMA_SUBALLOCATION_TYPE_UNKNOWN,-        allocationCount,-        pAllocations);--    if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)-    {-        for(size_t i = 0; i < allocationCount; ++i)-        {-            allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);-        }-    }--    return result;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(-    VmaAllocator allocator,-    VkBuffer buffer,-    const VmaAllocationCreateInfo* pCreateInfo,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);--    VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VkMemoryRequirements vkMemReq = {};-    bool requiresDedicatedAllocation = false;-    bool prefersDedicatedAllocation = false;-    allocator->GetBufferMemoryRequirements(buffer, vkMemReq,-        requiresDedicatedAllocation,-        prefersDedicatedAllocation);--    VkResult result = allocator->AllocateMemory(-        vkMemReq,-        requiresDedicatedAllocation,-        prefersDedicatedAllocation,-        buffer, // dedicatedBuffer-        VK_NULL_HANDLE, // dedicatedImage-        UINT32_MAX, // dedicatedBufferImageUsage-        *pCreateInfo,-        VMA_SUBALLOCATION_TYPE_BUFFER,-        1, // allocationCount-        pAllocation);--    if(pAllocationInfo && result == VK_SUCCESS)-    {-        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-    }--    return result;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(-    VmaAllocator allocator,-    VkImage image,-    const VmaAllocationCreateInfo* pCreateInfo,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);--    VMA_DEBUG_LOG("vmaAllocateMemoryForImage");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    VkMemoryRequirements vkMemReq = {};-    bool requiresDedicatedAllocation = false;-    bool prefersDedicatedAllocation  = false;-    allocator->GetImageMemoryRequirements(image, vkMemReq,-        requiresDedicatedAllocation, prefersDedicatedAllocation);--    VkResult result = allocator->AllocateMemory(-        vkMemReq,-        requiresDedicatedAllocation,-        prefersDedicatedAllocation,-        VK_NULL_HANDLE, // dedicatedBuffer-        image, // dedicatedImage-        UINT32_MAX, // dedicatedBufferImageUsage-        *pCreateInfo,-        VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,-        1, // allocationCount-        pAllocation);--    if(pAllocationInfo && result == VK_SUCCESS)-    {-        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-    }--    return result;-}--VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(-    VmaAllocator allocator,-    VmaAllocation allocation)-{-    VMA_ASSERT(allocator);--    if(allocation == VK_NULL_HANDLE)-    {-        return;-    }--    VMA_DEBUG_LOG("vmaFreeMemory");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->FreeMemory(-        1, // allocationCount-        &allocation);-}--VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(-    VmaAllocator allocator,-    size_t allocationCount,-    const VmaAllocation* pAllocations)-{-    if(allocationCount == 0)-    {-        return;-    }--    VMA_ASSERT(allocator);--    VMA_DEBUG_LOG("vmaFreeMemoryPages");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->FreeMemory(allocationCount, pAllocations);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && allocation && pAllocationInfo);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->GetAllocationInfo(allocation, pAllocationInfo);-}--VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(-    VmaAllocator allocator,-    VmaAllocation allocation,-    void* pUserData)-{-    VMA_ASSERT(allocator && allocation);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocation->SetUserData(allocator, pUserData);-}--VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const char* VMA_NULLABLE pName)-{-    allocation->SetName(allocator, pName);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)-{-    VMA_ASSERT(allocator && allocation && pFlags);-    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();-    *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(-    VmaAllocator allocator,-    VmaAllocation allocation,-    void** ppData)-{-    VMA_ASSERT(allocator && allocation && ppData);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->Map(allocation, ppData);-}--VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(-    VmaAllocator allocator,-    VmaAllocation allocation)-{-    VMA_ASSERT(allocator && allocation);--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    allocator->Unmap(allocation);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkDeviceSize offset,-    VkDeviceSize size)-{-    VMA_ASSERT(allocator && allocation);--    VMA_DEBUG_LOG("vmaFlushAllocation");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);--    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkDeviceSize offset,-    VkDeviceSize size)-{-    VMA_ASSERT(allocator && allocation);--    VMA_DEBUG_LOG("vmaInvalidateAllocation");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);--    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(-    VmaAllocator allocator,-    uint32_t allocationCount,-    const VmaAllocation* allocations,-    const VkDeviceSize* offsets,-    const VkDeviceSize* sizes)-{-    VMA_ASSERT(allocator);--    if(allocationCount == 0)-    {-        return VK_SUCCESS;-    }--    VMA_ASSERT(allocations);--    VMA_DEBUG_LOG("vmaFlushAllocations");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);--    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(-    VmaAllocator allocator,-    uint32_t allocationCount,-    const VmaAllocation* allocations,-    const VkDeviceSize* offsets,-    const VkDeviceSize* sizes)-{-    VMA_ASSERT(allocator);--    if(allocationCount == 0)-    {-        return VK_SUCCESS;-    }--    VMA_ASSERT(allocations);--    VMA_DEBUG_LOG("vmaInvalidateAllocations");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);--    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(-    VmaAllocator allocator,-    uint32_t memoryTypeBits)-{-    VMA_ASSERT(allocator);--    VMA_DEBUG_LOG("vmaCheckCorruption");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->CheckCorruption(memoryTypeBits);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(-    VmaAllocator allocator,-    const VmaDefragmentationInfo* pInfo,-    VmaDefragmentationContext* pContext)-{-    VMA_ASSERT(allocator && pInfo && pContext);--    VMA_DEBUG_LOG("vmaBeginDefragmentation");--    if (pInfo->pool != VMA_NULL)-    {-        // Check if run on supported algorithms-        if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)-            return VK_ERROR_FEATURE_NOT_PRESENT;-    }--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);-    return VK_SUCCESS;-}--VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(-    VmaAllocator allocator,-    VmaDefragmentationContext context,-    VmaDefragmentationStats* pStats)-{-    VMA_ASSERT(allocator && context);--    VMA_DEBUG_LOG("vmaEndDefragmentation");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    if (pStats)-        context->GetStats(*pStats);-    vma_delete(allocator, context);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaDefragmentationContext VMA_NOT_NULL context,-    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)-{-    VMA_ASSERT(context && pPassInfo);--    VMA_DEBUG_LOG("vmaBeginDefragmentationPass");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return context->DefragmentPassBegin(*pPassInfo);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaDefragmentationContext VMA_NOT_NULL context,-    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)-{-    VMA_ASSERT(context && pPassInfo);--    VMA_DEBUG_LOG("vmaEndDefragmentationPass");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return context->DefragmentPassEnd(*pPassInfo);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkBuffer buffer)-{-    VMA_ASSERT(allocator && allocation && buffer);--    VMA_DEBUG_LOG("vmaBindBufferMemory");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkDeviceSize allocationLocalOffset,-    VkBuffer buffer,-    const void* pNext)-{-    VMA_ASSERT(allocator && allocation && buffer);--    VMA_DEBUG_LOG("vmaBindBufferMemory2");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkImage image)-{-    VMA_ASSERT(allocator && allocation && image);--    VMA_DEBUG_LOG("vmaBindImageMemory");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(-    VmaAllocator allocator,-    VmaAllocation allocation,-    VkDeviceSize allocationLocalOffset,-    VkImage image,-    const void* pNext)-{-    VMA_ASSERT(allocator && allocation && image);--    VMA_DEBUG_LOG("vmaBindImageMemory2");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--        return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(-    VmaAllocator allocator,-    const VkBufferCreateInfo* pBufferCreateInfo,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    VkBuffer* pBuffer,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);--    if(pBufferCreateInfo->size == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }-    if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&-        !allocator->m_UseKhrBufferDeviceAddress)-    {-        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VMA_DEBUG_LOG("vmaCreateBuffer");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    *pBuffer = VK_NULL_HANDLE;-    *pAllocation = VK_NULL_HANDLE;--    // 1. Create VkBuffer.-    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(-        allocator->m_hDevice,-        pBufferCreateInfo,-        allocator->GetAllocationCallbacks(),-        pBuffer);-    if(res >= 0)-    {-        // 2. vkGetBufferMemoryRequirements.-        VkMemoryRequirements vkMemReq = {};-        bool requiresDedicatedAllocation = false;-        bool prefersDedicatedAllocation  = false;-        allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,-            requiresDedicatedAllocation, prefersDedicatedAllocation);--        // 3. Allocate memory using allocator.-        res = allocator->AllocateMemory(-            vkMemReq,-            requiresDedicatedAllocation,-            prefersDedicatedAllocation,-            *pBuffer, // dedicatedBuffer-            VK_NULL_HANDLE, // dedicatedImage-            pBufferCreateInfo->usage, // dedicatedBufferImageUsage-            *pAllocationCreateInfo,-            VMA_SUBALLOCATION_TYPE_BUFFER,-            1, // allocationCount-            pAllocation);--        if(res >= 0)-        {-            // 3. Bind buffer with memory.-            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)-            {-                res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);-            }-            if(res >= 0)-            {-                // All steps succeeded.-                #if VMA_STATS_STRING_ENABLED-                    (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);-                #endif-                if(pAllocationInfo != VMA_NULL)-                {-                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-                }--                return VK_SUCCESS;-            }-            allocator->FreeMemory(-                1, // allocationCount-                pAllocation);-            *pAllocation = VK_NULL_HANDLE;-            (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());-            *pBuffer = VK_NULL_HANDLE;-            return res;-        }-        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());-        *pBuffer = VK_NULL_HANDLE;-        return res;-    }-    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(-    VmaAllocator allocator,-    const VkBufferCreateInfo* pBufferCreateInfo,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    VkDeviceSize minAlignment,-    VkBuffer* pBuffer,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);--    if(pBufferCreateInfo->size == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }-    if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&-        !allocator->m_UseKhrBufferDeviceAddress)-    {-        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    *pBuffer = VK_NULL_HANDLE;-    *pAllocation = VK_NULL_HANDLE;--    // 1. Create VkBuffer.-    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(-        allocator->m_hDevice,-        pBufferCreateInfo,-        allocator->GetAllocationCallbacks(),-        pBuffer);-    if(res >= 0)-    {-        // 2. vkGetBufferMemoryRequirements.-        VkMemoryRequirements vkMemReq = {};-        bool requiresDedicatedAllocation = false;-        bool prefersDedicatedAllocation  = false;-        allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,-            requiresDedicatedAllocation, prefersDedicatedAllocation);--        // 2a. Include minAlignment-        vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);--        // 3. Allocate memory using allocator.-        res = allocator->AllocateMemory(-            vkMemReq,-            requiresDedicatedAllocation,-            prefersDedicatedAllocation,-            *pBuffer, // dedicatedBuffer-            VK_NULL_HANDLE, // dedicatedImage-            pBufferCreateInfo->usage, // dedicatedBufferImageUsage-            *pAllocationCreateInfo,-            VMA_SUBALLOCATION_TYPE_BUFFER,-            1, // allocationCount-            pAllocation);--        if(res >= 0)-        {-            // 3. Bind buffer with memory.-            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)-            {-                res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);-            }-            if(res >= 0)-            {-                // All steps succeeded.-                #if VMA_STATS_STRING_ENABLED-                    (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage);-                #endif-                if(pAllocationInfo != VMA_NULL)-                {-                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-                }--                return VK_SUCCESS;-            }-            allocator->FreeMemory(-                1, // allocationCount-                pAllocation);-            *pAllocation = VK_NULL_HANDLE;-            (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());-            *pBuffer = VK_NULL_HANDLE;-            return res;-        }-        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());-        *pBuffer = VK_NULL_HANDLE;-        return res;-    }-    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)-{-    return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,-    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)-{-    VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);-    VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize());--    VMA_DEBUG_LOG("vmaCreateAliasingBuffer2");--    *pBuffer = VK_NULL_HANDLE;--    if (pBufferCreateInfo->size == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }-    if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&-        !allocator->m_UseKhrBufferDeviceAddress)-    {-        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    // 1. Create VkBuffer.-    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(-        allocator->m_hDevice,-        pBufferCreateInfo,-        allocator->GetAllocationCallbacks(),-        pBuffer);-    if (res >= 0)-    {-        // 2. Bind buffer with memory.-        res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL);-        if (res >= 0)-        {-            return VK_SUCCESS;-        }-        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());-    }-    return res;-}--VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(-    VmaAllocator allocator,-    VkBuffer buffer,-    VmaAllocation allocation)-{-    VMA_ASSERT(allocator);--    if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)-    {-        return;-    }--    VMA_DEBUG_LOG("vmaDestroyBuffer");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    if(buffer != VK_NULL_HANDLE)-    {-        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());-    }--    if(allocation != VK_NULL_HANDLE)-    {-        allocator->FreeMemory(-            1, // allocationCount-            &allocation);-    }-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(-    VmaAllocator allocator,-    const VkImageCreateInfo* pImageCreateInfo,-    const VmaAllocationCreateInfo* pAllocationCreateInfo,-    VkImage* pImage,-    VmaAllocation* pAllocation,-    VmaAllocationInfo* pAllocationInfo)-{-    VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);--    if(pImageCreateInfo->extent.width == 0 ||-        pImageCreateInfo->extent.height == 0 ||-        pImageCreateInfo->extent.depth == 0 ||-        pImageCreateInfo->mipLevels == 0 ||-        pImageCreateInfo->arrayLayers == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VMA_DEBUG_LOG("vmaCreateImage");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    *pImage = VK_NULL_HANDLE;-    *pAllocation = VK_NULL_HANDLE;--    // 1. Create VkImage.-    VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(-        allocator->m_hDevice,-        pImageCreateInfo,-        allocator->GetAllocationCallbacks(),-        pImage);-    if(res >= 0)-    {-        VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?-            VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :-            VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;--        // 2. Allocate memory using allocator.-        VkMemoryRequirements vkMemReq = {};-        bool requiresDedicatedAllocation = false;-        bool prefersDedicatedAllocation  = false;-        allocator->GetImageMemoryRequirements(*pImage, vkMemReq,-            requiresDedicatedAllocation, prefersDedicatedAllocation);--        res = allocator->AllocateMemory(-            vkMemReq,-            requiresDedicatedAllocation,-            prefersDedicatedAllocation,-            VK_NULL_HANDLE, // dedicatedBuffer-            *pImage, // dedicatedImage-            pImageCreateInfo->usage, // dedicatedBufferImageUsage-            *pAllocationCreateInfo,-            suballocType,-            1, // allocationCount-            pAllocation);--        if(res >= 0)-        {-            // 3. Bind image with memory.-            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)-            {-                res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);-            }-            if(res >= 0)-            {-                // All steps succeeded.-                #if VMA_STATS_STRING_ENABLED-                    (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage);-                #endif-                if(pAllocationInfo != VMA_NULL)-                {-                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);-                }--                return VK_SUCCESS;-            }-            allocator->FreeMemory(-                1, // allocationCount-                pAllocation);-            *pAllocation = VK_NULL_HANDLE;-            (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());-            *pImage = VK_NULL_HANDLE;-            return res;-        }-        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());-        *pImage = VK_NULL_HANDLE;-        return res;-    }-    return res;-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)-{-    return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(-    VmaAllocator VMA_NOT_NULL allocator,-    VmaAllocation VMA_NOT_NULL allocation,-    VkDeviceSize allocationLocalOffset,-    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)-{-    VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);--    *pImage = VK_NULL_HANDLE;--    VMA_DEBUG_LOG("vmaCreateImage2");--    if (pImageCreateInfo->extent.width == 0 ||-        pImageCreateInfo->extent.height == 0 ||-        pImageCreateInfo->extent.depth == 0 ||-        pImageCreateInfo->mipLevels == 0 ||-        pImageCreateInfo->arrayLayers == 0)-    {-        return VK_ERROR_INITIALIZATION_FAILED;-    }--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    // 1. Create VkImage.-    VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(-        allocator->m_hDevice,-        pImageCreateInfo,-        allocator->GetAllocationCallbacks(),-        pImage);-    if (res >= 0)-    {-        // 2. Bind image with memory.-        res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL);-        if (res >= 0)-        {-            return VK_SUCCESS;-        }-        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());-    }-    return res;-}--VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(-    VmaAllocator VMA_NOT_NULL allocator,-    VkImage VMA_NULLABLE_NON_DISPATCHABLE image,-    VmaAllocation VMA_NULLABLE allocation)-{-    VMA_ASSERT(allocator);--    if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)-    {-        return;-    }--    VMA_DEBUG_LOG("vmaDestroyImage");--    VMA_DEBUG_GLOBAL_MUTEX_LOCK--    if(image != VK_NULL_HANDLE)-    {-        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());-    }-    if(allocation != VK_NULL_HANDLE)-    {-        allocator->FreeMemory(-            1, // allocationCount-            &allocation);-    }-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(-    const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,-    VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)-{-    VMA_ASSERT(pCreateInfo && pVirtualBlock);-    VMA_ASSERT(pCreateInfo->size > 0);-    VMA_DEBUG_LOG("vmaCreateVirtualBlock");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);-    VkResult res = (*pVirtualBlock)->Init();-    if(res < 0)-    {-        vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);-        *pVirtualBlock = VK_NULL_HANDLE;-    }-    return res;-}--VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)-{-    if(virtualBlock != VK_NULL_HANDLE)-    {-        VMA_DEBUG_LOG("vmaDestroyVirtualBlock");-        VMA_DEBUG_GLOBAL_MUTEX_LOCK;-        VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.-        vma_delete(&allocationCallbacks, virtualBlock);-    }-}--VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);-    VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);-    VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);-}--VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,-    VkDeviceSize* VMA_NULLABLE pOffset)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);-    VMA_DEBUG_LOG("vmaVirtualAllocate");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);-}--VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)-{-    if(allocation != VK_NULL_HANDLE)-    {-        VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);-        VMA_DEBUG_LOG("vmaVirtualFree");-        VMA_DEBUG_GLOBAL_MUTEX_LOCK;-        virtualBlock->Free(allocation);-    }-}--VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);-    VMA_DEBUG_LOG("vmaClearVirtualBlock");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    virtualBlock->Clear();-}--VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);-    VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    virtualBlock->SetAllocationUserData(allocation, pUserData);-}--VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaStatistics* VMA_NOT_NULL pStats)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);-    VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    virtualBlock->GetStatistics(*pStats);-}--VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    VmaDetailedStatistics* VMA_NOT_NULL pStats)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);-    VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    virtualBlock->CalculateDetailedStatistics(*pStats);-}--#if VMA_STATS_STRING_ENABLED--VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)-{-    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);-    VMA_DEBUG_GLOBAL_MUTEX_LOCK;-    const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();-    VmaStringBuilder sb(allocationCallbacks);-    virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);-    *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());-}--VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,-    char* VMA_NULLABLE pStatsString)-{-    if(pStatsString != VMA_NULL)-    {-        VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);-        VMA_DEBUG_GLOBAL_MUTEX_LOCK;-        VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);-    }-}-#endif // VMA_STATS_STRING_ENABLED-#endif // _VMA_PUBLIC_INTERFACE-#endif // VMA_IMPLEMENTATION--/**-\page quick_start Quick start--\section quick_start_project_setup Project setup--Vulkan Memory Allocator comes in form of a "stb-style" single header file.-You don't need to build it as a separate library project.-You can add this file directly to your project and submit it to code repository next to your other source files.--"Single header" doesn't mean that everything is contained in C/C++ declarations,-like it tends to be in case of inline functions or C++ templates.-It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.-If you don't do it properly, you will get linker errors.--To do it properly:---# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.-   This includes declarations of all members of the library.--# In exactly one CPP file define following macro before this include.-   It enables also internal definitions.--\code-#define VMA_IMPLEMENTATION-#include "vk_mem_alloc.h"-\endcode--It may be a good idea to create dedicated CPP file just for this purpose.--This library includes header `<vulkan/vulkan.h>`, which in turn-includes `<windows.h>` on Windows. If you need some specific macros defined-before including these headers (like `WIN32_LEAN_AND_MEAN` or-`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define-them before every `#include` of this library.--This library is written in C++, but has C-compatible interface.-Thus you can include and use vk_mem_alloc.h in C or C++ code, but full-implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.-Some features of C++14 are used. STL containers, RTTI, or C++ exceptions are not used.---\section quick_start_initialization Initialization--At program startup:---# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object.--# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by-   calling vmaCreateAllocator().--Only members `physicalDevice`, `device`, `instance` are required.-However, you should inform the library which Vulkan version do you use by setting-VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable-by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address).-Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.--\subsection quick_start_initialization_selecting_vulkan_version Selecting Vulkan version--VMA supports Vulkan version down to 1.0, for backward compatibility.-If you want to use higher version, you need to inform the library about it.-This is a two-step process.--<b>Step 1: Compile time.</b> By default, VMA compiles with code supporting the highest-Vulkan version found in the included `<vulkan/vulkan.h>` that is also supported by the library.-If this is OK, you don't need to do anything.-However, if you want to compile VMA as if only some lower Vulkan version was available,-define macro `VMA_VULKAN_VERSION` before every `#include "vk_mem_alloc.h"`.-It should have decimal numeric value in form of ABBBCCC, where A = major, BBB = minor, CCC = patch Vulkan version.-For example, to compile against Vulkan 1.2:--\code-#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2-#include "vk_mem_alloc.h"-\endcode--<b>Step 2: Runtime.</b> Even when compiled with higher Vulkan version available,-VMA can use only features of a lower version, which is configurable during creation of the #VmaAllocator object.-By default, only Vulkan 1.0 is used.-To initialize the allocator with support for higher Vulkan version, you need to set member-VmaAllocatorCreateInfo::vulkanApiVersion to an appropriate value, e.g. using constants like `VK_API_VERSION_1_2`.-See code sample below.--\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions--You may need to configure importing Vulkan functions. There are 3 ways to do this:---# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):-   - You don't need to do anything.-   - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default.--# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`,-   `vkGetDeviceProcAddr` (this is the option presented in the example below):-   - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1.-   - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,-     VmaVulkanFunctions::vkGetDeviceProcAddr.-   - The library will fetch pointers to all other functions it needs internally.--# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like-   [Volk](https://github.com/zeux/volk):-   - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.-   - Pass these pointers via structure #VmaVulkanFunctions.--Example for case 2:--\code-#define VMA_STATIC_VULKAN_FUNCTIONS 0-#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1-#include "vk_mem_alloc.h"--...--VmaVulkanFunctions vulkanFunctions = {};-vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;-vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;--VmaAllocatorCreateInfo allocatorCreateInfo = {};-allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;-allocatorCreateInfo.physicalDevice = physicalDevice;-allocatorCreateInfo.device = device;-allocatorCreateInfo.instance = instance;-allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;--VmaAllocator allocator;-vmaCreateAllocator(&allocatorCreateInfo, &allocator);-\endcode---\section quick_start_resource_allocation Resource allocation--When you want to create a buffer or image:---# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.--# Fill VmaAllocationCreateInfo structure.--# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory-   already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory.--\code-VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufferInfo.size = 65536;-bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocInfo = {};-allocInfo.usage = VMA_MEMORY_USAGE_AUTO;--VkBuffer buffer;-VmaAllocation allocation;-vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);-\endcode--Don't forget to destroy your objects when no longer needed:--\code-vmaDestroyBuffer(allocator, buffer, allocation);-vmaDestroyAllocator(allocator);-\endcode---\page choosing_memory_type Choosing memory type--Physical devices in Vulkan support various combinations of memory heaps and-types. Help with choosing correct and optimal memory type for your specific-resource is one of the key features of this library. You can use it by filling-appropriate members of VmaAllocationCreateInfo structure, as described below.-You can also combine multiple methods.---# If you just want to find memory type index that meets your requirements, you-   can use function: vmaFindMemoryTypeIndexForBufferInfo(),-   vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex().--# If you want to allocate a region of device memory without association with any-   specific image or buffer, you can use function vmaAllocateMemory(). Usage of-   this function is not recommended and usually not needed.-   vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,-   which may be useful for sparse binding.--# If you already have a buffer or an image created, you want to allocate memory-   for it and then you will bind it yourself, you can use function-   vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().-   For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()-   or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().--# **This is the easiest and recommended way to use this library:**-   If you want to create a buffer or an image, allocate memory for it and bind-   them together, all in one call, you can use function vmaCreateBuffer(),-   vmaCreateImage().--When using 3. or 4., the library internally queries Vulkan for memory types-supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)-and uses only one of these types.--If no memory type can be found that meets all the requirements, these functions-return `VK_ERROR_FEATURE_NOT_PRESENT`.--You can leave VmaAllocationCreateInfo structure completely filled with zeros.-It means no requirements are specified for memory type.-It is valid, although not very useful.--\section choosing_memory_type_usage Usage--The easiest way to specify memory requirements is to fill member-VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.-It defines high level, common usage types.-Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.--For example, if you want to create a uniform buffer that will be filled using-transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can-do it using following code. The buffer will most likely end up in a memory type with-`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device.--\code-VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufferInfo.size = 65536;-bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocInfo = {};-allocInfo.usage = VMA_MEMORY_USAGE_AUTO;--VkBuffer buffer;-VmaAllocation allocation;-vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);-\endcode--If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory-on systems with discrete graphics card that have the memories separate, you can use-#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST.--When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory,-you also need to specify one of the host access flags:-#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.-This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`-so you can map it.--For example, a staging buffer that will be filled via mapped pointer and then-used as a source of transfer to the buffer described previously can be created like this.-It will likely end up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT`-but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM).--\code-VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-stagingBufferInfo.size = 65536;-stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;--VmaAllocationCreateInfo stagingAllocInfo = {};-stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;-stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;--VkBuffer stagingBuffer;-VmaAllocation stagingAllocation;-vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);-\endcode--For more examples of creating different kinds of resources, see chapter \ref usage_patterns.--Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows-about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed,-so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc.-If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting-memory type, as described below.--\note-Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`,-`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`)-are still available and work same way as in previous versions of the library-for backward compatibility, but they are not recommended.--\section choosing_memory_type_required_preferred_flags Required and preferred flags--You can specify more detailed requirements by filling members-VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags-with a combination of bits from enum `VkMemoryPropertyFlags`. For example,-if you want to create a buffer that will be persistently mapped on host (so it-must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,-use following code:--\code-VmaAllocationCreateInfo allocInfo = {};-allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;-allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;-allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;--VkBuffer buffer;-VmaAllocation allocation;-vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);-\endcode--A memory type is chosen that has all the required flags and as many preferred-flags set as possible.--Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags,-plus some extra "magic" (heuristics).--\section choosing_memory_type_explicit_memory_types Explicit memory types--If you inspected memory types available on the physical device and you have-a preference for memory types that you want to use, you can fill member-VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set-means that a memory type with that index is allowed to be used for the-allocation. Special value 0, just like `UINT32_MAX`, means there are no-restrictions to memory type index.--Please note that this member is NOT just a memory type index.-Still you can use it to choose just one, specific memory type.-For example, if you already determined that your buffer should be created in-memory type 2, use following code:--\code-uint32_t memoryTypeIndex = 2;--VmaAllocationCreateInfo allocInfo = {};-allocInfo.memoryTypeBits = 1u << memoryTypeIndex;--VkBuffer buffer;-VmaAllocation allocation;-vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);-\endcode---\section choosing_memory_type_custom_memory_pools Custom memory pools--If you allocate from custom memory pool, all the ways of specifying memory-requirements described above are not applicable and the aforementioned members-of VmaAllocationCreateInfo structure are ignored. Memory type is selected-explicitly when creating the pool and then used to make all the allocations from-that pool. For further details, see \ref custom_memory_pools.--\section choosing_memory_type_dedicated_allocations Dedicated allocations--Memory for allocations is reserved out of larger block of `VkDeviceMemory`-allocated from Vulkan internally. That is the main feature of this whole library.-You can still request a separate memory block to be created for an allocation,-just like you would do in a trivial solution without using any allocator.-In that case, a buffer or image is always bound to that memory at offset 0.-This is called a "dedicated allocation".-You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.-The library can also internally decide to use dedicated allocation in some cases, e.g.:--- When the size of the allocation is large.-- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled-  and it reports that dedicated allocation is required or recommended for the resource.-- When allocation of next big memory block fails due to not enough device memory,-  but allocation with the exact requested size succeeds.---\page memory_mapping Memory mapping--To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,-to be able to read from it or write to it in CPU code.-Mapping is possible only of memory allocated from a memory type that has-`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.-Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.-You can use them directly with memory allocated by this library,-but it is not recommended because of following issue:-Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.-This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.-Because of this, Vulkan Memory Allocator provides following facilities:--\note If you want to be able to map an allocation, you need to specify one of the flags-#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT-in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable-when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.-For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,-but they can still be used for consistency.--\section memory_mapping_mapping_functions Mapping functions--The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().-They are safer and more convenient to use than standard Vulkan functions.-You can map an allocation multiple times simultaneously - mapping is reference-counted internally.-You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.-The way it is implemented is that the library always maps entire memory block, not just region of the allocation.-For further details, see description of vmaMapMemory() function.-Example:--\code-// Having these objects initialized:-struct ConstantBuffer-{-    ...-};-ConstantBuffer constantBufferData = ...--VmaAllocator allocator = ...-VkBuffer constantBuffer = ...-VmaAllocation constantBufferAllocation = ...--// You can map and fill your buffer using following code:--void* mappedData;-vmaMapMemory(allocator, constantBufferAllocation, &mappedData);-memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));-vmaUnmapMemory(allocator, constantBufferAllocation);-\endcode--When mapping, you may see a warning from Vulkan validation layer similar to this one:--<i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>--It happens because the library maps entire `VkDeviceMemory` block, where different-types of images and buffers may end up together, especially on GPUs with unified memory like Intel.-You can safely ignore it if you are sure you access only memory of the intended-object that you wanted to map.---\section memory_mapping_persistently_mapped_memory Persistently mapped memory--Keeping your memory persistently mapped is generally OK in Vulkan.-You don't need to unmap it before using its data on the GPU.-The library provides a special feature designed for that:-Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in-VmaAllocationCreateInfo::flags stay mapped all the time,-so you can just access CPU pointer to it any time-without a need to call any "map" or "unmap" function.-Example:--\code-VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufCreateInfo.size = sizeof(ConstantBuffer);-bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |-    VMA_ALLOCATION_CREATE_MAPPED_BIT;--VkBuffer buf;-VmaAllocation alloc;-VmaAllocationInfo allocInfo;-vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);--// Buffer is already mapped. You can access its memory.-memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));-\endcode--\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up-in a mappable memory type.-For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or-#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.-#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation.-For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading.--\section memory_mapping_cache_control Cache flush and invalidate--Memory in Vulkan doesn't need to be unmapped before using it on GPU,-but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,-you need to manually **invalidate** cache before reading of mapped pointer-and **flush** cache after writing to mapped pointer.-Map/unmap operations don't do that automatically.-Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,-`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient-functions that refer to given allocation object: vmaFlushAllocation(),-vmaInvalidateAllocation(),-or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().--Regions of memory specified for flush/invalidate must be aligned to-`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.-In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations-within blocks are aligned to this value, so their offsets are always multiply of-`nonCoherentAtomSize` and two different allocations never share same "line" of this size.--Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)-currently provide `HOST_COHERENT` flag on all memory types that are-`HOST_VISIBLE`, so on PC you may not need to bother.---\page staying_within_budget Staying within budget--When developing a graphics-intensive game or program, it is important to avoid allocating-more GPU memory than it is physically available. When the memory is over-committed,-various bad things can happen, depending on the specific GPU, graphics driver, and-operating system:--- It may just work without any problems.-- The application may slow down because some memory blocks are moved to system RAM-  and the GPU has to access them through PCI Express bus.-- A new allocation may take very long time to complete, even few seconds, and possibly-  freeze entire system.-- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.-- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`-  returned somewhere later.--\section staying_within_budget_querying_for_budget Querying for budget--To query for current memory usage and available budget, use function vmaGetHeapBudgets().-Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.--Please note that this function returns different information and works faster than-vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every-allocation, while vmaCalculateStatistics() is intended to be used rarely,-only to obtain statistical information, e.g. for debugging purposes.--It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information-about the budget from Vulkan device. VMA is able to use this extension automatically.-When not enabled, the allocator behaves same way, but then it estimates current usage-and available budget based on its internal information and Vulkan memory heap sizes,-which may be less precise. In order to use this extension:--1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2-   required by it are available and enable them. Please note that the first is a device-   extension and the second is instance extension!-2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.-3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from-   Vulkan inside of it to avoid overhead of querying it with every allocation.--\section staying_within_budget_controlling_memory_usage Controlling memory usage--There are many ways in which you can try to stay within the budget.--First, when making new allocation requires allocating a new memory block, the library-tries not to exceed the budget automatically. If a block with default recommended size-(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even-dedicated memory for just this resource.--If the size of the requested resource plus current memory usage is more than the-budget, by default the library still tries to create it, leaving it to the Vulkan-implementation whether the allocation succeeds or fails. You can change this behavior-by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is-not made if it would exceed the budget or if the budget is already exceeded.-VMA then tries to make the allocation from the next eligible Vulkan memory type.-The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.-Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag-when creating resources that are not essential for the application (e.g. the texture-of a specific object) and not to pass it when creating critically important resources-(e.g. render targets).--On AMD graphics cards there is a custom vendor extension available: <b>VK_AMD_memory_overallocation_behavior</b>-that allows to control the behavior of the Vulkan implementation in out-of-memory cases --whether it should fail with an error code or still allow the allocation.-Usage of this extension involves only passing extra structure on Vulkan device creation,-so it is out of scope of this library.--Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure-a new allocation is created only when it fits inside one of the existing memory blocks.-If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.-This also ensures that the function call is very fast because it never goes to Vulkan-to obtain a new block.--\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount-set to more than 0 will currently try to allocate memory blocks without checking whether they-fit within budget.---\page resource_aliasing Resource aliasing (overlap)--New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory-management, give an opportunity to alias (overlap) multiple resources in the-same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).-It can be useful to save video memory, but it must be used with caution.--For example, if you know the flow of your whole render frame in advance, you-are going to use some intermediate textures or buffers only during a small range of render passes,-and you know these ranges don't overlap in time, you can bind these resources to-the same place in memory, even if they have completely different parameters (width, height, format etc.).--![Resource aliasing (overlap)](../gfx/Aliasing.png)--Such scenario is possible using VMA, but you need to create your images manually.-Then you need to calculate parameters of an allocation to be made using formula:--- allocation size = max(size of each image)-- allocation alignment = max(alignment of each image)-- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)--Following example shows two different images bound to the same place in memory,-allocated to fit largest of them.--\code-// A 512x512 texture to be sampled.-VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };-img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;-img1CreateInfo.extent.width = 512;-img1CreateInfo.extent.height = 512;-img1CreateInfo.extent.depth = 1;-img1CreateInfo.mipLevels = 10;-img1CreateInfo.arrayLayers = 1;-img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;-img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;-img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;-img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;-img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;--// A full screen texture to be used as color attachment.-VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };-img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;-img2CreateInfo.extent.width = 1920;-img2CreateInfo.extent.height = 1080;-img2CreateInfo.extent.depth = 1;-img2CreateInfo.mipLevels = 1;-img2CreateInfo.arrayLayers = 1;-img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;-img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;-img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;-img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;-img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;--VkImage img1;-res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);-VkImage img2;-res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);--VkMemoryRequirements img1MemReq;-vkGetImageMemoryRequirements(device, img1, &img1MemReq);-VkMemoryRequirements img2MemReq;-vkGetImageMemoryRequirements(device, img2, &img2MemReq);--VkMemoryRequirements finalMemReq = {};-finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);-finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);-finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;-// Validate if(finalMemReq.memoryTypeBits != 0)--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;--VmaAllocation alloc;-res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);--res = vmaBindImageMemory(allocator, alloc, img1);-res = vmaBindImageMemory(allocator, alloc, img2);--// You can use img1, img2 here, but not at the same time!--vmaFreeMemory(allocator, alloc);-vkDestroyImage(allocator, img2, nullptr);-vkDestroyImage(allocator, img1, nullptr);-\endcode--VMA also provides convenience functions that create a buffer or image and bind it to memory-represented by an existing #VmaAllocation:-vmaCreateAliasingBuffer(), vmaCreateAliasingBuffer2(),-vmaCreateAliasingImage(), vmaCreateAliasingImage2().-Versions with "2" offer additional parameter `allocationLocalOffset`.--Remember that using resources that alias in memory requires proper synchronization.-You need to issue a memory barrier to make sure commands that use `img1` and `img2`-don't overlap on GPU timeline.-You also need to treat a resource after aliasing as uninitialized - containing garbage data.-For example, if you use `img1` and then want to use `img2`, you need to issue-an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.--Additional considerations:--- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.-See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.-- You can create more complex layout where different images and buffers are bound-at different offsets inside one large allocation. For example, one can imagine-a big texture used in some render passes, aliasing with a set of many small buffers-used between in some further passes. To bind a resource at non-zero offset in an allocation,-use vmaBindBufferMemory2() / vmaBindImageMemory2().-- Before allocating memory for the resources you want to alias, check `memoryTypeBits`-returned in memory requirements of each resource to make sure the bits overlap.-Some GPUs may expose multiple memory types suitable e.g. only for buffers or-images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your-resources may be disjoint. Aliasing them is not possible in that case.---\page custom_memory_pools Custom memory pools--A memory pool contains a number of `VkDeviceMemory` blocks.-The library automatically creates and manages default pool for each memory type available on the device.-Default memory pool automatically grows in size.-Size of allocated blocks is also variable and managed automatically.--You can create custom pool and allocate memory out of it.-It can be useful if you want to:--- Keep certain kind of allocations separate from others.-- Enforce particular, fixed size of Vulkan memory blocks.-- Limit maximum amount of Vulkan memory allocated for that pool.-- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.-- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in-  #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.-- Perform defragmentation on a specific subset of your allocations.--To use custom memory pools:---# Fill VmaPoolCreateInfo structure.--# Call vmaCreatePool() to obtain #VmaPool handle.--# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.-   You don't need to specify any other parameters of this structure, like `usage`.--Example:--\code-// Find memoryTypeIndex for the pool.-VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-sampleBufCreateInfo.size = 0x10000; // Doesn't matter.-sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo sampleAllocCreateInfo = {};-sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;--uint32_t memTypeIndex;-VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator,-    &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex);-// Check res...--// Create a pool that can have at most 2 blocks, 128 MiB each.-VmaPoolCreateInfo poolCreateInfo = {};-poolCreateInfo.memoryTypeIndex = memTypeIndex;-poolCreateInfo.blockSize = 128ull * 1024 * 1024;-poolCreateInfo.maxBlockCount = 2;--VmaPool pool;-res = vmaCreatePool(allocator, &poolCreateInfo, &pool);-// Check res...--// Allocate a buffer out of it.-VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufCreateInfo.size = 1024;-bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.pool = pool;--VkBuffer buf;-VmaAllocation alloc;-res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);-// Check res...-\endcode--You have to free all allocations made from this pool before destroying it.--\code-vmaDestroyBuffer(allocator, buf, alloc);-vmaDestroyPool(allocator, pool);-\endcode--New versions of this library support creating dedicated allocations in custom pools.-It is supported only when VmaPoolCreateInfo::blockSize = 0.-To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and-VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.--\note Excessive use of custom pools is a common mistake when using this library.-Custom pools may be useful for special purposes - when you want to-keep certain type of resources separate e.g. to reserve minimum amount of memory-for them or limit maximum amount of memory they can occupy. For most-resources this is not needed and so it is not recommended to create #VmaPool-objects and allocations out of them. Allocating from the default pool is sufficient.---\section custom_memory_pools_MemTypeIndex Choosing memory type index--When creating a pool, you must explicitly specify memory type index.-To find the one suitable for your buffers or images, you can use helper functions-vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().-You need to provide structures with example parameters of buffers or images-that you are going to create in that pool.--\code-VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-exampleBufCreateInfo.size = 1024; // Doesn't matter-exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;--uint32_t memTypeIndex;-vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);--VmaPoolCreateInfo poolCreateInfo = {};-poolCreateInfo.memoryTypeIndex = memTypeIndex;-// ...-\endcode--When creating buffers/images allocated in that pool, provide following parameters:--- `VkBufferCreateInfo`: Prefer to pass same parameters as above.-  Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.-  Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers-  or the other way around.-- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.-  Other members are ignored anyway.--\section linear_algorithm Linear allocation algorithm--Each Vulkan memory block managed by this library has accompanying metadata that-keeps track of used and unused regions. By default, the metadata structure and-algorithm tries to find best place for new allocations among free regions to-optimize memory usage. This way you can allocate and free objects in any order.--![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)--Sometimes there is a need to use simpler, linear allocation algorithm. You can-create custom pool that uses such algorithm by adding flag-#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating-#VmaPool object. Then an alternative metadata management is used. It always-creates new allocations after last one and doesn't reuse free regions after-allocations freed in the middle. It results in better allocation performance and-less memory consumed by metadata.--![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)--With this one flag, you can create a custom pool that can be used in many ways:-free-at-once, stack, double stack, and ring buffer. See below for details.-You don't need to specify explicitly which of these options you are going to use - it is detected automatically.--\subsection linear_algorithm_free_at_once Free-at-once--In a pool that uses linear algorithm, you still need to free all the allocations-individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free-them in any order. New allocations are always made after last one - free space-in the middle is not reused. However, when you release all the allocation and-the pool becomes empty, allocation starts from the beginning again. This way you-can use linear algorithm to speed up creation of allocations that you are going-to release all at once.--![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)--This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount-value that allows multiple memory blocks.--\subsection linear_algorithm_stack Stack--When you free an allocation that was created last, its space can be reused.-Thanks to this, if you always release allocations in the order opposite to their-creation (LIFO - Last In First Out), you can achieve behavior of a stack.--![Stack](../gfx/Linear_allocator_4_stack.png)--This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount-value that allows multiple memory blocks.--\subsection linear_algorithm_double_stack Double stack--The space reserved by a custom pool with linear algorithm may be used by two-stacks:--- First, default one, growing up from offset 0.-- Second, "upper" one, growing down from the end towards lower offsets.--To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT-to VmaAllocationCreateInfo::flags.--![Double stack](../gfx/Linear_allocator_7_double_stack.png)--Double stack is available only in pools with one memory block --VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.--When the two stacks' ends meet so there is not enough space between them for a-new allocation, such allocation fails with usual-`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.--\subsection linear_algorithm_ring_buffer Ring buffer--When you free some allocations from the beginning and there is not enough free space-for a new one at the end of a pool, allocator's "cursor" wraps around to the-beginning and starts allocation there. Thanks to this, if you always release-allocations in the same order as you created them (FIFO - First In First Out),-you can achieve behavior of a ring buffer / queue.--![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)--Ring buffer is available only in pools with one memory block --VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.--\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.---\page defragmentation Defragmentation--Interleaved allocations and deallocations of many objects of varying size can-cause fragmentation over time, which can lead to a situation where the library is unable-to find a continuous range of free memory for a new allocation despite there is-enough free space, just scattered across many small free ranges between existing-allocations.--To mitigate this problem, you can use defragmentation feature.-It doesn't happen automatically though and needs your cooperation,-because VMA is a low level library that only allocates memory.-It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures.-It cannot copy their contents as it doesn't record any commands to a command buffer.--Example:--\code-VmaDefragmentationInfo defragInfo = {};-defragInfo.pool = myPool;-defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT;--VmaDefragmentationContext defragCtx;-VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);-// Check res...--for(;;)-{-    VmaDefragmentationPassMoveInfo pass;-    res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);-    if(res == VK_SUCCESS)-        break;-    else if(res != VK_INCOMPLETE)-        // Handle error...--    for(uint32_t i = 0; i < pass.moveCount; ++i)-    {-        // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents.-        VmaAllocationInfo allocInfo;-        vmaGetAllocationInfo(allocator, pass.pMoves[i].srcAllocation, &allocInfo);-        MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData;--        // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.-        VkImageCreateInfo imgCreateInfo = ...-        VkImage newImg;-        res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg);-        // Check res...-        res = vmaBindImageMemory(allocator, pass.pMoves[i].dstTmpAllocation, newImg);-        // Check res...--        // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.-        vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...);-    }--    // Make sure the copy commands finished executing.-    vkWaitForFences(...);--    // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation.-    for(uint32_t i = 0; i < pass.moveCount; ++i)-    {-        // ...-        vkDestroyImage(device, resData->img, nullptr);-    }--    // Update appropriate descriptors to point to the new places...--    res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);-    if(res == VK_SUCCESS)-        break;-    else if(res != VK_INCOMPLETE)-        // Handle error...-}--vmaEndDefragmentation(allocator, defragCtx, nullptr);-\endcode--Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage()-create/destroy an allocation and a buffer/image at once, these are just a shortcut for-creating the resource, allocating memory, and binding them together.-Defragmentation works on memory allocations only. You must handle the rest manually.-Defragmentation is an iterative process that should repreat "passes" as long as related functions-return `VK_INCOMPLETE` not `VK_SUCCESS`.-In each pass:--1. vmaBeginDefragmentationPass() function call:-   - Calculates and returns the list of allocations to be moved in this pass.-     Note this can be a time-consuming process.-   - Reserves destination memory for them by creating temporary destination allocations-     that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo().-2. Inside the pass, **you should**:-   - Inspect the returned list of allocations to be moved.-   - Create new buffers/images and bind them at the returned destination temporary allocations.-   - Copy data from source to destination resources if necessary.-   - Destroy the source buffers/images, but NOT their allocations.-3. vmaEndDefragmentationPass() function call:-   - Frees the source memory reserved for the allocations that are moved.-   - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory.-   - Frees `VkDeviceMemory` blocks that became empty.--Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter.-Defragmentation algorithm tries to move all suitable allocations.-You can, however, refuse to move some of them inside a defragmentation pass, by setting-`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.-This is not recommended and may result in suboptimal packing of the allocations after defragmentation.-If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.--Inside a pass, for each allocation that should be moved:--- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`.-  - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass().-- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared,-  filled, and used temporarily in each rendering frame, you can just recreate this image-  without copying its data.-- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU-  using `memcpy()`.-- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.-  This will cancel the move.-  - vmaEndDefragmentationPass() will then free the destination memory-    not the source memory of the allocation, leaving it unchanged.-- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),-  you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.-  - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object.--You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool-(like in the example above) or all the default pools by setting this member to null.--Defragmentation is always performed in each pool separately.-Allocations are never moved between different Vulkan memory types.-The size of the destination memory reserved for a moved allocation is the same as the original one.-Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation.-Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones.--You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved-in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.-See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.--It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA-usage, possibly from multiple threads, with the exception that allocations-returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.--<b>Mapping</b> is preserved on allocations that are moved during defragmentation.-Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations-are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried-using VmaAllocationInfo::pMappedData.--\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.---\page statistics Statistics--This library contains several functions that return information about its internal state,-especially the amount of memory allocated from Vulkan.--\section statistics_numeric_statistics Numeric statistics--If you need to obtain basic statistics about memory usage per heap, together with current budget,-you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget.-This is useful to keep track of memory usage and stay within budget-(see also \ref staying_within_budget).-Example:--\code-uint32_t heapIndex = ...--VmaBudget budgets[VK_MAX_MEMORY_HEAPS];-vmaGetHeapBudgets(allocator, budgets);--printf("My heap currently has %u allocations taking %llu B,\n",-    budgets[heapIndex].statistics.allocationCount,-    budgets[heapIndex].statistics.allocationBytes);-printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n",-    budgets[heapIndex].statistics.blockCount,-    budgets[heapIndex].statistics.blockBytes);-printf("Vulkan reports total usage %llu B with budget %llu B.\n",-    budgets[heapIndex].usage,-    budgets[heapIndex].budget);-\endcode--You can query for more detailed statistics per memory heap, type, and totals,-including minimum and maximum allocation size and unused range size,-by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics.-This function is slower though, as it has to traverse all the internal data structures,-so it should be used only for debugging purposes.--You can query for statistics of a custom pool using function vmaGetPoolStatistics()-or vmaCalculatePoolStatistics().--You can query for information about a specific allocation using function vmaGetAllocationInfo().-It fill structure #VmaAllocationInfo.--\section statistics_json_dump JSON dump--You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().-The result is guaranteed to be correct JSON.-It uses ANSI encoding.-Any strings provided by user (see [Allocation names](@ref allocation_names))-are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,-this JSON string can be treated as using this encoding.-It must be freed using function vmaFreeStatsString().--The format of this JSON string is not part of official documentation of the library,-but it will not change in backward-incompatible way without increasing library major version number-and appropriate mention in changelog.--The JSON string contains all the data that can be obtained using vmaCalculateStatistics().-It can also contain detailed map of allocated memory blocks and their regions --free and occupied by allocations.-This allows e.g. to visualize the memory or assess fragmentation.---\page allocation_annotation Allocation names and user data--\section allocation_user_data Allocation user data--You can annotate allocations with your own information, e.g. for debugging purposes.-To do that, fill VmaAllocationCreateInfo::pUserData field when creating-an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,-some handle, index, key, ordinal number or any other value that would associate-the allocation with your custom metadata.-It is useful to identify appropriate data structures in your engine given #VmaAllocation,-e.g. when doing \ref defragmentation.--\code-VkBufferCreateInfo bufCreateInfo = ...--MyBufferMetadata* pMetadata = CreateBufferMetadata();--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.pUserData = pMetadata;--VkBuffer buffer;-VmaAllocation allocation;-vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr);-\endcode--The pointer may be later retrieved as VmaAllocationInfo::pUserData:--\code-VmaAllocationInfo allocInfo;-vmaGetAllocationInfo(allocator, allocation, &allocInfo);-MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;-\endcode--It can also be changed using function vmaSetAllocationUserData().--Values of (non-zero) allocations' `pUserData` are printed in JSON report created by-vmaBuildStatsString() in hexadecimal form.--\section allocation_names Allocation names--An allocation can also carry a null-terminated string, giving a name to the allocation.-To set it, call vmaSetAllocationName().-The library creates internal copy of the string, so the pointer you pass doesn't need-to be valid for whole lifetime of the allocation. You can free it after the call.--\code-std::string imageName = "Texture: ";-imageName += fileName;-vmaSetAllocationName(allocator, allocation, imageName.c_str());-\endcode--The string can be later retrieved by inspecting VmaAllocationInfo::pName.-It is also printed in JSON report created by vmaBuildStatsString().--\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.-You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.---\page virtual_allocator Virtual allocator--As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".-It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".-You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.-A common use case is sub-allocation of pieces of one large GPU buffer.--\section virtual_allocator_creating_virtual_block Creating virtual block--To use this functionality, there is no main "allocator" object.-You don't need to have #VmaAllocator object created.-All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator:---# Fill in #VmaVirtualBlockCreateInfo structure.--# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.--Example:--\code-VmaVirtualBlockCreateInfo blockCreateInfo = {};-blockCreateInfo.size = 1048576; // 1 MB--VmaVirtualBlock block;-VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);-\endcode--\section virtual_allocator_making_virtual_allocations Making virtual allocations--#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions-using the same code as the main Vulkan memory allocator.-Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type-that represents an opaque handle to an allocation within the virtual block.--In order to make such allocation:---# Fill in #VmaVirtualAllocationCreateInfo structure.--# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation.-   You can also receive `VkDeviceSize offset` that was assigned to the allocation.--Example:--\code-VmaVirtualAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.size = 4096; // 4 KB--VmaVirtualAllocation alloc;-VkDeviceSize offset;-res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset);-if(res == VK_SUCCESS)-{-    // Use the 4 KB of your memory starting at offset.-}-else-{-    // Allocation failed - no space for it could be found. Handle this error!-}-\endcode--\section virtual_allocator_deallocation Deallocation--When no longer needed, an allocation can be freed by calling vmaVirtualFree().-You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate()-called for the same #VmaVirtualBlock.--When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().-All allocations must be freed before the block is destroyed, which is checked internally by an assert.-However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once --a feature not available in normal Vulkan memory allocator. Example:--\code-vmaVirtualFree(block, alloc);-vmaDestroyVirtualBlock(block);-\endcode--\section virtual_allocator_allocation_parameters Allocation parameters--You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().-Its default value is null.-It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some-larger data structure containing more information. Example:--\code-struct CustomAllocData-{-    std::string m_AllocName;-};-CustomAllocData* allocData = new CustomAllocData();-allocData->m_AllocName = "My allocation 1";-vmaSetVirtualAllocationUserData(block, alloc, allocData);-\endcode--The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function-vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.-If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!-Example:--\code-VmaVirtualAllocationInfo allocInfo;-vmaGetVirtualAllocationInfo(block, alloc, &allocInfo);-delete (CustomAllocData*)allocInfo.pUserData;--vmaVirtualFree(block, alloc);-\endcode--\section virtual_allocator_alignment_and_units Alignment and units--It feels natural to express sizes and offsets in bytes.-If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member-VmaVirtualAllocationCreateInfo::alignment to request it. Example:--\code-VmaVirtualAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.size = 4096; // 4 KB-allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B--VmaVirtualAllocation alloc;-res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr);-\endcode--Alignments of different allocations made from one block may vary.-However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,-you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.-It might be more convenient, but you need to make sure to use this new unit consistently in all the places:--- VmaVirtualBlockCreateInfo::size-- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment-- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset--\section virtual_allocator_statistics Statistics--You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics()-(to get brief statistics that are fast to calculate)-or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate).-The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator.-Example:--\code-VmaStatistics stats;-vmaGetVirtualBlockStatistics(block, &stats);-printf("My virtual block has %llu bytes used by %u virtual allocations\n",-    stats.allocationBytes, stats.allocationCount);-\endcode--You can also request a full list of allocations and free regions as a string in JSON format by calling-vmaBuildVirtualBlockStatsString().-Returned string must be later freed using vmaFreeVirtualBlockStatsString().-The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.--\section virtual_allocator_additional_considerations Additional considerations--The "virtual allocator" functionality is implemented on a level of individual memory blocks.-Keeping track of a whole collection of blocks, allocating new ones when out of free space,-deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.--Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.-See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).-You can find their description in chapter \ref custom_memory_pools.-Allocation strategies are also supported.-See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).--Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:-buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.---\page debugging_memory_usage Debugging incorrect memory usage--If you suspect a bug with memory usage, like usage of uninitialized memory or-memory being overwritten out of bounds of an allocation,-you can use debug features of this library to verify this.--\section debugging_memory_usage_initialization Memory initialization--If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,-you can enable automatic memory initialization to verify this.-To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.--\code-#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1-#include "vk_mem_alloc.h"-\endcode--It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`.-Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.-Memory is automatically mapped and unmapped if necessary.--If you find these values while debugging your program, good chances are that you incorrectly-read Vulkan memory that is allocated but not initialized, or already freed, respectively.--Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped.-It works also with dedicated allocations.--\section debugging_memory_usage_margins Margins--By default, allocations are laid out in memory blocks next to each other if possible-(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).--![Allocations without margin](../gfx/Margins_1.png)--Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified-number of bytes as a margin after every allocation.--\code-#define VMA_DEBUG_MARGIN 16-#include "vk_mem_alloc.h"-\endcode--![Allocations with margin](../gfx/Margins_2.png)--If your bug goes away after enabling margins, it means it may be caused by memory-being overwritten outside of allocation boundaries. It is not 100% certain though.-Change in application behavior may also be caused by different order and distribution-of allocations across memory blocks after margins are applied.--Margins work with all types of memory.--Margin is applied only to allocations made out of memory blocks and not to dedicated-allocations, which have their own memory block of specific size.-It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag-or those automatically decided to put into dedicated allocations, e.g. due to its-large size or recommended by VK_KHR_dedicated_allocation extension.--Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.--Note that enabling margins increases memory usage and fragmentation.--Margins do not apply to \ref virtual_allocator.--\section debugging_memory_usage_corruption_detection Corruption detection--You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation-of contents of the margins.--\code-#define VMA_DEBUG_MARGIN 16-#define VMA_DEBUG_DETECT_CORRUPTION 1-#include "vk_mem_alloc.h"-\endcode--When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`-(it must be multiply of 4) after every allocation is filled with a magic number.-This idea is also know as "canary".-Memory is automatically mapped and unmapped if necessary.--This number is validated automatically when the allocation is destroyed.-If it is not equal to the expected value, `VMA_ASSERT()` is executed.-It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,-which indicates a serious bug.--You can also explicitly request checking margins of all allocations in all memory blocks-that belong to specified memory types by using function vmaCheckCorruption(),-or in memory blocks that belong to specified custom pool, by using function-vmaCheckPoolCorruption().--Margin validation (corruption detection) works only for memory types that are-`HOST_VISIBLE` and `HOST_COHERENT`.---\page opengl_interop OpenGL Interop--VMA provides some features that help with interoperability with OpenGL.--\section opengl_interop_exporting_memory Exporting memory--If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library:--It is recommended to create \ref custom_memory_pools for such allocations.-Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext-while creating the custom pool.-Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,-not only while creating it, as no copy of the structure is made,-but its original pointer is used for each allocation instead.--If you want to export all memory allocated by the library from certain memory types,-also dedicated allocations or other allocations made from default pools,-an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.-It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library-through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.-Please note that new versions of the library also support dedicated allocations created in custom pools.--You should not mix these two methods in a way that allows to apply both to the same memory type.-Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.---\section opengl_interop_custom_alignment Custom alignment--Buffers or images exported to a different API like OpenGL may require a different alignment,-higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.-To impose such alignment:--It is recommended to create \ref custom_memory_pools for such allocations.-Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation-to be made out of this pool.-The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image-from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.--If you want to create a buffer with a specific minimum alignment out of default pools,-use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.--Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated-allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.-Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.---\page usage_patterns Recommended usage patterns--Vulkan gives great flexibility in memory allocation.-This chapter shows the most common patterns.--See also slides from talk:-[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)---\section usage_patterns_gpu_only GPU-only resource--<b>When:</b>-Any resources that you frequently write and read on GPU,-e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,-images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").--<b>What to do:</b>-Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.--\code-VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };-imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;-imgCreateInfo.extent.width = 3840;-imgCreateInfo.extent.height = 2160;-imgCreateInfo.extent.depth = 1;-imgCreateInfo.mipLevels = 1;-imgCreateInfo.arrayLayers = 1;-imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;-imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;-imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;-imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;-imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;-allocCreateInfo.priority = 1.0f;--VkImage img;-VmaAllocation alloc;-vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);-\endcode--<b>Also consider:</b>-Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,-especially if they are large or if you plan to destroy and recreate them with different sizes-e.g. when display resolution changes.-Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.-When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation-to decrease chances to be evicted to system memory by the operating system.--\section usage_patterns_staging_copy_upload Staging copy for upload--<b>When:</b>-A "staging" buffer than you want to map and fill from CPU code, then use as a source of transfer-to some GPU resource.--<b>What to do:</b>-Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT.-Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`.--\code-VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufCreateInfo.size = 65536;-bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |-    VMA_ALLOCATION_CREATE_MAPPED_BIT;--VkBuffer buf;-VmaAllocation alloc;-VmaAllocationInfo allocInfo;-vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);--...--memcpy(allocInfo.pMappedData, myData, myDataSize);-\endcode--<b>Also consider:</b>-You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped-using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above.---\section usage_patterns_readback Readback--<b>When:</b>-Buffers for data written by or transferred from the GPU that you want to read back on the CPU,-e.g. results of some computations.--<b>What to do:</b>-Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.-Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`-and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.--\code-VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufCreateInfo.size = 65536;-bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |-    VMA_ALLOCATION_CREATE_MAPPED_BIT;--VkBuffer buf;-VmaAllocation alloc;-VmaAllocationInfo allocInfo;-vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);--...--const float* downloadedData = (const float*)allocInfo.pMappedData;-\endcode---\section usage_patterns_advanced_data_uploading Advanced data uploading--For resources that you frequently write on CPU via mapped pointer and-frequently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible:---# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory,-   even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card,-   and make the device reach out to that resource directly.-   - Reads performed by the device will then go through PCI Express bus.-     The performance of this access may be limited, but it may be fine depending on the size-     of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity-     of access.--# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips),-   a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL`-   (fast to access from the GPU). Then, it is likely the best choice for such type of resource.--# Systems with a discrete graphics card and separate video memory may or may not expose-   a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR).-   If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS)-   that is available to CPU for mapping.-   - Writes performed by the host to that memory go through PCI Express bus.-     The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0,-     as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads.--# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory,-   a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them.--Thankfully, VMA offers an aid to create and use such resources in the the way optimal-for the current Vulkan device. To help the library make the best choice,-use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with-#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT.-It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR),-but if no such memory type is available or allocation from it fails-(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS),-it will fall back to `DEVICE_LOCAL` memory for fast GPU access.-It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`,-so you need to create another "staging" allocation and perform explicit transfers.--\code-VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-bufCreateInfo.size = 65536;-bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |-    VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |-    VMA_ALLOCATION_CREATE_MAPPED_BIT;--VkBuffer buf;-VmaAllocation alloc;-VmaAllocationInfo allocInfo;-vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);--VkMemoryPropertyFlags memPropFlags;-vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);--if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)-{-    // Allocation ended up in a mappable memory and is already mapped - write to it directly.--    // [Executed in runtime]:-    memcpy(allocInfo.pMappedData, myData, myDataSize);-}-else-{-    // Allocation ended up in a non-mappable memory - need to transfer.-    VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };-    stagingBufCreateInfo.size = 65536;-    stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;--    VmaAllocationCreateInfo stagingAllocCreateInfo = {};-    stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-    stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |-        VMA_ALLOCATION_CREATE_MAPPED_BIT;--    VkBuffer stagingBuf;-    VmaAllocation stagingAlloc;-    VmaAllocationInfo stagingAllocInfo;-    vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,-        &stagingBuf, &stagingAlloc, stagingAllocInfo);--    // [Executed in runtime]:-    memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);-    vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);-    //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT-    VkBufferCopy bufCopy = {-        0, // srcOffset-        0, // dstOffset,-        myDataSize); // size-    vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);-}-\endcode--\section usage_patterns_other_use_cases Other use cases--Here are some other, less obvious use cases and their recommended settings:--- An image that is used only as transfer source and destination, but it should stay on the device,-  as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame,-  for temporal antialiasing or other temporal effects.-  - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`-  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO-- An image that is used only as transfer source and destination, but it should be placed-  in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict-  least recently used textures from VRAM.-  - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`-  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST,-    as VMA needs a hint here to differentiate from the previous case.-- A buffer that you want to map and write from the CPU, directly read from the GPU-  (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or-  host memory due to its large size.-  - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`-  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST-  - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT---\page configuration Configuration--Please check "CONFIGURATION SECTION" in the code to find macros that you can define-before each include of this file or change directly in this file to provide-your own implementation of basic facilities like assert, `min()` and `max()` functions,-mutex, atomic etc.-The library uses its own implementation of containers by default, but you can switch to using-STL containers instead.--For example, define `VMA_ASSERT(expr)` before including the library to provide-custom implementation of the assertion, compatible with your project.-By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration-and empty otherwise.--\section config_Vulkan_functions Pointers to Vulkan functions--There are multiple ways to import pointers to Vulkan functions in the library.-In the simplest case you don't need to do anything.-If the compilation or linking of your program or the initialization of the #VmaAllocator-doesn't work for you, you can try to reconfigure it.--First, the allocator tries to fetch pointers to Vulkan functions linked statically,-like this:--\code-m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;-\endcode--If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.--Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.-You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or-by using a helper library like [volk](https://github.com/zeux/volk).--Third, VMA tries to fetch remaining pointers that are still null by calling-`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.-You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr.-Other pointers will be fetched automatically.-If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.--Finally, all the function pointers required by the library (considering selected-Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.---\section custom_memory_allocator Custom host memory allocator--If you use custom allocator for CPU memory rather than default operator `new`-and `delete` from C++, you can make this library using your allocator as well-by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These-functions will be passed to Vulkan, as well as used by the library itself to-make any CPU-side allocations.--\section allocation_callbacks Device memory allocation callbacks--The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.-You can setup callbacks to be informed about these calls, e.g. for the purpose-of gathering some statistics. To do it, fill optional member-VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.--\section heap_memory_limit Device heap memory limit--When device memory of certain heap runs out of free space, new allocations may-fail (returning error code) or they may succeed, silently pushing some existing_-memory blocks from GPU VRAM to system RAM (which degrades performance). This-behavior is implementation-dependent - it depends on GPU vendor and graphics-driver.--On AMD cards it can be controlled while creating Vulkan device object by using-VK_AMD_memory_overallocation_behavior extension, if available.--Alternatively, if you want to test how your program behaves with limited amount of Vulkan device-memory available without switching your graphics card to one that really has-smaller VRAM, you can use a feature of this library intended for this purpose.-To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.----\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation--VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve-performance on some GPUs. It augments Vulkan API with possibility to query-driver whether it prefers particular buffer or image to have its own, dedicated-allocation (separate `VkDeviceMemory` block) for better efficiency - to be able-to do some internal optimizations. The extension is supported by this library.-It will be used automatically when enabled.--It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version-and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion,-you are all set.--Otherwise, if you want to use it as an extension:--1 . When creating Vulkan device, check if following 2 device extensions are-supported (call `vkEnumerateDeviceExtensionProperties()`).-If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).--- VK_KHR_get_memory_requirements2-- VK_KHR_dedicated_allocation--If you enabled these extensions:--2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating-your #VmaAllocator to inform the library that you enabled required extensions-and you want the library to use them.--\code-allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;--vmaCreateAllocator(&allocatorInfo, &allocator);-\endcode--That is all. The extension will be automatically used whenever you create a-buffer using vmaCreateBuffer() or image using vmaCreateImage().--When using the extension together with Vulkan Validation Layer, you will receive-warnings like this:--_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._--It is OK, you should just ignore it. It happens because you use function-`vkGetBufferMemoryRequirements2KHR()` instead of standard-`vkGetBufferMemoryRequirements()`, while the validation layer seems to be-unaware of it.--To learn more about this extension, see:--- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)-- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)----\page vk_ext_memory_priority VK_EXT_memory_priority--VK_EXT_memory_priority is a device extension that allows to pass additional "priority"-value to Vulkan memory allocations that the implementation may use prefer certain-buffers and images that are critical for performance to stay in device-local memory-in cases when the memory is over-subscribed, while some others may be moved to the system memory.--VMA offers convenient usage of this extension.-If you enable it, you can pass "priority" parameter when creating allocations or custom pools-and the library automatically passes the value to Vulkan using this extension.--If you want to use this extension in connection with VMA, follow these steps:--\section vk_ext_memory_priority_initialization Initialization--1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.-Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority".--2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.-Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned.-Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true.--3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority"-to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.--4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.-Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.-Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to-`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`.--5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you-have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT-to VmaAllocatorCreateInfo::flags.--\section vk_ext_memory_priority_usage Usage--When using this extension, you should initialize following member:--- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.-- VmaPoolCreateInfo::priority when creating a custom pool.--It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`.-Memory allocated with higher value can be treated by the Vulkan implementation as higher priority-and so it can have lower chances of being pushed out to system memory, experiencing degraded performance.--It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images-as dedicated and set high priority to them. For example:--\code-VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };-imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;-imgCreateInfo.extent.width = 3840;-imgCreateInfo.extent.height = 2160;-imgCreateInfo.extent.depth = 1;-imgCreateInfo.mipLevels = 1;-imgCreateInfo.arrayLayers = 1;-imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;-imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;-imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;-imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;-imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;--VmaAllocationCreateInfo allocCreateInfo = {};-allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;-allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;-allocCreateInfo.priority = 1.0f;--VkImage img;-VmaAllocation alloc;-vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);-\endcode--`priority` member is ignored in the following situations:--- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters-  from the parametrs passed in #VmaPoolCreateInfo when the pool was created.-- Allocations created in default pools: They inherit the priority from the parameters-  VMA used when creating default pools, which means `priority == 0.5f`.---\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory--VK_AMD_device_coherent_memory is a device extension that enables access to-additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and-`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for-allocation of buffers intended for writing "breadcrumb markers" in between passes-or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.--When the extension is available but has not been enabled, Vulkan physical device-still exposes those memory types, but their usage is forbidden. VMA automatically-takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt-to allocate memory of such type is made.--If you want to use this extension in connection with VMA, follow these steps:--\section vk_amd_device_coherent_memory_initialization Initialization--1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.-Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".--2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.-Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.-Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.--3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"-to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.--4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.-Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.-Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to-`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.--5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you-have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT-to VmaAllocatorCreateInfo::flags.--\section vk_amd_device_coherent_memory_usage Usage--After following steps described above, you can create VMA allocations and custom pools-out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible-devices. There are multiple ways to do it, for example:--- You can request or prefer to allocate out of such memory types by adding-  `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags-  or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with-  other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.-- If you manually found memory type index to use for this purpose, force allocation-  from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`.--\section vk_amd_device_coherent_memory_more_information More information--To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html)--Example use of this extension can be found in the code of the sample and test suite-accompanying this library.+// Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All rights reserved.+//+// 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.+//++#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H+#define AMD_VULKAN_MEMORY_ALLOCATOR_H++/** \mainpage Vulkan Memory Allocator++<b>Version 3.3.0</b>++Copyright (c) 2017-2025 Advanced Micro Devices, Inc. All rights reserved. \n+License: MIT \n+See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-memory-allocator/),+[repository on GitHub](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)+++<b>API documentation divided into groups:</b> [Topics](topics.html)++<b>General documentation chapters:</b>++- \subpage faq+- \subpage quick_start+  - [Project setup](@ref quick_start_project_setup)+  - [Initialization](@ref quick_start_initialization)+  - [Resource allocation](@ref quick_start_resource_allocation)+- \subpage choosing_memory_type+  - [Usage](@ref choosing_memory_type_usage)+  - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags)+  - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types)+  - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)+  - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)+- \subpage memory_mapping+  - [Copy functions](@ref memory_mapping_copy_functions)+  - [Mapping functions](@ref memory_mapping_mapping_functions)+  - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)+  - [Cache flush and invalidate](@ref memory_mapping_cache_control)+- \subpage staying_within_budget+  - [Querying for budget](@ref staying_within_budget_querying_for_budget)+  - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage)+- \subpage resource_aliasing+- \subpage custom_memory_pools+  - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex)+  - [When not to use custom pools](@ref custom_memory_pools_when_not_use)+  - [Linear allocation algorithm](@ref linear_algorithm)+    - [Free-at-once](@ref linear_algorithm_free_at_once)+    - [Stack](@ref linear_algorithm_stack)+    - [Double stack](@ref linear_algorithm_double_stack)+    - [Ring buffer](@ref linear_algorithm_ring_buffer)+- \subpage defragmentation+- \subpage statistics+  - [Numeric statistics](@ref statistics_numeric_statistics)+  - [JSON dump](@ref statistics_json_dump)+- \subpage allocation_annotation+  - [Allocation user data](@ref allocation_user_data)+  - [Allocation names](@ref allocation_names)+- \subpage virtual_allocator+- \subpage debugging_memory_usage+  - [Memory initialization](@ref debugging_memory_usage_initialization)+  - [Margins](@ref debugging_memory_usage_margins)+  - [Corruption detection](@ref debugging_memory_usage_corruption_detection)+  - [Leak detection features](@ref debugging_memory_usage_leak_detection)+- \subpage other_api_interop+- \subpage usage_patterns+    - [GPU-only resource](@ref usage_patterns_gpu_only)+    - [Staging copy for upload](@ref usage_patterns_staging_copy_upload)+    - [Readback](@ref usage_patterns_readback)+    - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading)+    - [Other use cases](@ref usage_patterns_other_use_cases)+- \subpage configuration+  - [Pointers to Vulkan functions](@ref config_Vulkan_functions)+  - [Custom host memory allocator](@ref custom_memory_allocator)+  - [Device memory allocation callbacks](@ref allocation_callbacks)+  - [Device heap memory limit](@ref heap_memory_limit)+- <b>Extension support</b>+    - \subpage vk_khr_dedicated_allocation+    - \subpage enabling_buffer_device_address+    - \subpage vk_ext_memory_priority+    - \subpage vk_amd_device_coherent_memory+    - \subpage vk_khr_external_memory_win32+- \subpage general_considerations+  - [Thread safety](@ref general_considerations_thread_safety)+  - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility)+  - [Validation layer warnings](@ref general_considerations_validation_layer_warnings)+  - [Allocation algorithm](@ref general_considerations_allocation_algorithm)+  - [Features not supported](@ref general_considerations_features_not_supported)++\defgroup group_init Library initialization++\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object.++\defgroup group_alloc Memory allocation++\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images.+Most basic ones being: vmaCreateBuffer(), vmaCreateImage().++\defgroup group_virtual Virtual allocator++\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm+for user-defined purpose without allocating any real GPU memory.++\defgroup group_stats Statistics++\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format.+See documentation chapter: \ref statistics.+*/+++#ifdef __cplusplus+extern "C" {+#endif++#if !defined(VULKAN_H_)+#include <vulkan/vulkan.h>+#endif++#if !defined(VMA_VULKAN_VERSION)+    #if defined(VK_VERSION_1_4)+        #define VMA_VULKAN_VERSION 1004000+    #elif defined(VK_VERSION_1_3)+        #define VMA_VULKAN_VERSION 1003000+    #elif defined(VK_VERSION_1_2)+        #define VMA_VULKAN_VERSION 1002000+    #elif defined(VK_VERSION_1_1)+        #define VMA_VULKAN_VERSION 1001000+    #else+        #define VMA_VULKAN_VERSION 1000000+    #endif+#endif++#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS+    extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;+    extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;+    extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;+    extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;+    extern PFN_vkAllocateMemory vkAllocateMemory;+    extern PFN_vkFreeMemory vkFreeMemory;+    extern PFN_vkMapMemory vkMapMemory;+    extern PFN_vkUnmapMemory vkUnmapMemory;+    extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges;+    extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges;+    extern PFN_vkBindBufferMemory vkBindBufferMemory;+    extern PFN_vkBindImageMemory vkBindImageMemory;+    extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;+    extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;+    extern PFN_vkCreateBuffer vkCreateBuffer;+    extern PFN_vkDestroyBuffer vkDestroyBuffer;+    extern PFN_vkCreateImage vkCreateImage;+    extern PFN_vkDestroyImage vkDestroyImage;+    extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer;+    #if VMA_VULKAN_VERSION >= 1001000+        extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2;+        extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2;+        extern PFN_vkBindBufferMemory2 vkBindBufferMemory2;+        extern PFN_vkBindImageMemory2 vkBindImageMemory2;+        extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2;+    #endif // #if VMA_VULKAN_VERSION >= 1001000+#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES++#if !defined(VMA_DEDICATED_ALLOCATION)+    #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation+        #define VMA_DEDICATED_ALLOCATION 1+    #else+        #define VMA_DEDICATED_ALLOCATION 0+    #endif+#endif++#if !defined(VMA_BIND_MEMORY2)+    #if VK_KHR_bind_memory2+        #define VMA_BIND_MEMORY2 1+    #else+        #define VMA_BIND_MEMORY2 0+    #endif+#endif++#if !defined(VMA_MEMORY_BUDGET)+    #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000)+        #define VMA_MEMORY_BUDGET 1+    #else+        #define VMA_MEMORY_BUDGET 0+    #endif+#endif++// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers.+#if !defined(VMA_BUFFER_DEVICE_ADDRESS)+    #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000+        #define VMA_BUFFER_DEVICE_ADDRESS 1+    #else+        #define VMA_BUFFER_DEVICE_ADDRESS 0+    #endif+#endif++// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers.+#if !defined(VMA_MEMORY_PRIORITY)+    #if VK_EXT_memory_priority+        #define VMA_MEMORY_PRIORITY 1+    #else+        #define VMA_MEMORY_PRIORITY 0+    #endif+#endif++// Defined to 1 when VK_KHR_maintenance4 device extension is defined in Vulkan headers.+#if !defined(VMA_KHR_MAINTENANCE4)+    #if VK_KHR_maintenance4+        #define VMA_KHR_MAINTENANCE4 1+    #else+        #define VMA_KHR_MAINTENANCE4 0+    #endif+#endif++// Defined to 1 when VK_KHR_maintenance5 device extension is defined in Vulkan headers.+#if !defined(VMA_KHR_MAINTENANCE5)+    #if VK_KHR_maintenance5+        #define VMA_KHR_MAINTENANCE5 1+    #else+        #define VMA_KHR_MAINTENANCE5 0+    #endif+#endif+++// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers.+#if !defined(VMA_EXTERNAL_MEMORY)+    #if VK_KHR_external_memory+        #define VMA_EXTERNAL_MEMORY 1+    #else+        #define VMA_EXTERNAL_MEMORY 0+    #endif+#endif++// Defined to 1 when VK_KHR_external_memory_win32 device extension is defined in Vulkan headers.+#if !defined(VMA_EXTERNAL_MEMORY_WIN32)+    #if VK_KHR_external_memory_win32+        #define VMA_EXTERNAL_MEMORY_WIN32 1+    #else+        #define VMA_EXTERNAL_MEMORY_WIN32 0+    #endif+#endif++// Define these macros to decorate all public functions with additional code,+// before and after returned type, appropriately. This may be useful for+// exporting the functions when compiling VMA as a separate library. Example:+// #define VMA_CALL_PRE  __declspec(dllexport)+// #define VMA_CALL_POST __cdecl+#ifndef VMA_CALL_PRE+    #define VMA_CALL_PRE+#endif+#ifndef VMA_CALL_POST+    #define VMA_CALL_POST+#endif++// Define this macro to decorate pNext pointers with an attribute specifying the Vulkan+// structure that will be extended via the pNext chain.+#ifndef VMA_EXTENDS_VK_STRUCT+    #define VMA_EXTENDS_VK_STRUCT(vkStruct)+#endif++// Define this macro to decorate pointers with an attribute specifying the+// length of the array they point to if they are not null.+//+// The length may be one of+// - The name of another parameter in the argument list where the pointer is declared+// - The name of another member in the struct where the pointer is declared+// - The name of a member of a struct type, meaning the value of that member in+//   the context of the call. For example+//   VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"),+//   this means the number of memory heaps available in the device associated+//   with the VmaAllocator being dealt with.+#ifndef VMA_LEN_IF_NOT_NULL+    #define VMA_LEN_IF_NOT_NULL(len)+#endif++// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang.+// see: https://clang.llvm.org/docs/AttributeReference.html#nullable+#ifndef VMA_NULLABLE+    #ifdef __clang__+        #define VMA_NULLABLE _Nullable+    #else+        #define VMA_NULLABLE+    #endif+#endif++// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang.+// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull+#ifndef VMA_NOT_NULL+    #ifdef __clang__+        #define VMA_NOT_NULL _Nonnull+    #else+        #define VMA_NOT_NULL+    #endif+#endif++// If non-dispatchable handles are represented as pointers then we can give+// then nullability annotations+#ifndef VMA_NOT_NULL_NON_DISPATCHABLE+    #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)+        #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL+    #else+        #define VMA_NOT_NULL_NON_DISPATCHABLE+    #endif+#endif++#ifndef VMA_NULLABLE_NON_DISPATCHABLE+    #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)+        #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE+    #else+        #define VMA_NULLABLE_NON_DISPATCHABLE+    #endif+#endif++#ifndef VMA_STATS_STRING_ENABLED+    #define VMA_STATS_STRING_ENABLED 1+#endif++////////////////////////////////////////////////////////////////////////////////+////////////////////////////////////////////////////////////////////////////////+//+//    INTERFACE+//+////////////////////////////////////////////////////////////////////////////////+////////////////////////////////////////////////////////////////////////////////++// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE.+#ifndef _VMA_ENUM_DECLARATIONS++/**+\addtogroup group_init+@{+*/++/// Flags for created #VmaAllocator.+typedef enum VmaAllocatorCreateFlagBits+{+    /** \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.+    */+    VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001,+    /** \brief Enables usage of VK_KHR_dedicated_allocation extension.++    The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.+    When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1.++    Using this extension 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+    VmaAllocatorCreateInfo::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.+    */+    VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002,+    /**+    Enables usage of VK_KHR_bind_memory2 extension.++    The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`.+    When it is `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 VmaAllocatorCreateInfo::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 vmaBindBufferMemory2() or vmaBindImageMemory2().+    */+    VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004,+    /**+    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 VmaAllocatorCreateInfo::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.+    */+    VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008,+    /**+    Enables usage of VK_AMD_device_coherent_memory extension.++    You may set this flag only if you:++    - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device,+    - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device,+    - want it to be used internally by this library.++    The extension and accompanying device feature provide access to memory types with+    `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags.+    They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR.++    When the extension is not enabled, such memory types are still enumerated, but their usage is illegal.+    To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type,+    returning `VK_ERROR_FEATURE_NOT_PRESENT`.+    */+    VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010,+    /**+    Enables usage of "buffer device address" feature, which allows you to use function+    `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader.++    You may set this flag only if you:++    1. (For Vulkan version < 1.2) Found as available and enabled device extension+    VK_KHR_buffer_device_address.+    This extension is promoted to core Vulkan 1.2.+    2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`.++    When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA.+    The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to+    allocated memory blocks wherever it might be needed.++    For more information, see documentation chapter \ref enabling_buffer_device_address.+    */+    VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020,+    /**+    Enables usage of VK_EXT_memory_priority extension in the library.++    You may set this flag only if you found available and enabled this device extension,+    along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`,+    while creating Vulkan device passed as VmaAllocatorCreateInfo::device.++    When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority+    are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored.++    A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.+    Larger values are higher priority. The granularity of the priorities is implementation-dependent.+    It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`.+    The value to be used for default priority is 0.5.+    For more details, see the documentation of the VK_EXT_memory_priority extension.+    */+    VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040,+    /**+    Enables usage of VK_KHR_maintenance4 extension in the library.++    You may set this flag only if you found available and enabled this device extension,+    while creating Vulkan device passed as VmaAllocatorCreateInfo::device.+    */+    VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT = 0x00000080,+    /**+    Enables usage of VK_KHR_maintenance5 extension in the library.++    You should set this flag if you found available and enabled this device extension,+    while creating Vulkan device passed as VmaAllocatorCreateInfo::device.+    */+    VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT = 0x00000100,++    /**+    Enables usage of VK_KHR_external_memory_win32 extension in the library.++    You should set this flag if you found available and enabled this device extension,+    while creating Vulkan device passed as VmaAllocatorCreateInfo::device.+    For more information, see \ref vk_khr_external_memory_win32.+    */+    VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT = 0x00000200,++    VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaAllocatorCreateFlagBits;+/// See #VmaAllocatorCreateFlagBits.+typedef VkFlags VmaAllocatorCreateFlags;++/** @} */++/**+\addtogroup group_alloc+@{+*/++/// \brief Intended usage of the allocated memory.+typedef enum VmaMemoryUsage+{+    /** No intended memory usage specified.+    Use other members of VmaAllocationCreateInfo to specify your requirements.+    */+    VMA_MEMORY_USAGE_UNKNOWN = 0,+    /**+    \deprecated Obsolete, preserved for backward compatibility.+    Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.+    */+    VMA_MEMORY_USAGE_GPU_ONLY = 1,+    /**+    \deprecated Obsolete, preserved for backward compatibility.+    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`.+    */+    VMA_MEMORY_USAGE_CPU_ONLY = 2,+    /**+    \deprecated Obsolete, preserved for backward compatibility.+    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.+    */+    VMA_MEMORY_USAGE_CPU_TO_GPU = 3,+    /**+    \deprecated Obsolete, preserved for backward compatibility.+    Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.+    */+    VMA_MEMORY_USAGE_GPU_TO_CPU = 4,+    /**+    \deprecated Obsolete, preserved for backward compatibility.+    Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.+    */+    VMA_MEMORY_USAGE_CPU_COPY = 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.+    */+    VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6,+    /**+    Selects best memory type automatically.+    This flag is recommended for most common use cases.++    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),+    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT+    in VmaAllocationCreateInfo::flags.++    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.+    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()+    and not with generic memory allocation functions.+    */+    VMA_MEMORY_USAGE_AUTO = 7,+    /**+    Selects best memory type automatically with preference for GPU (device) memory.++    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),+    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT+    in VmaAllocationCreateInfo::flags.++    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.+    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()+    and not with generic memory allocation functions.+    */+    VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8,+    /**+    Selects best memory type automatically with preference for CPU (host) memory.++    When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT),+    you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT+    in VmaAllocationCreateInfo::flags.++    It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g.+    vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo()+    and not with generic memory allocation functions.+    */+    VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9,++    VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF+} VmaMemoryUsage;++/// Flags to be passed as VmaAllocationCreateInfo::flags.+typedef enum VmaAllocationCreateFlagBits+{+    /** \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.++    If you use this flag while creating a buffer or an image, `VkMemoryDedicatedAllocateInfo`+    structure is applied if possible.+    */+    VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001,++    /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block.++    If new allocation cannot be placed in any of the existing blocks, allocation+    fails with `VK_ERROR_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.+    */+    VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002,+    /** \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 VmaAllocationInfo::pMappedData.++    It is 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).+    */+    VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004,+    /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead.++    Set this flag to treat VmaAllocationCreateInfo::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 `pName`. The string is automatically+    freed together with the allocation. It is also used in vmaBuildStatsString().+    */+    VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020,+    /** 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.+    */+    VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,+    /** 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: vmaCreateBuffer(), vmaCreateImage().+    Otherwise it is ignored.++    If you want to make sure the new buffer/image is not tied to the new memory allocation+    through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block,+    use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT.+    */+    VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080,+    /** Create allocation only if additional device memory required for it, if any, won't exceed+    memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`.+    */+    VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100,+    /** \brief Set this flag if the allocated memory will have aliasing resources.++    Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified.+    Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors.+    */+    VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200,+    /**+    Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).++    - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,+      you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.+    - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.+      This includes allocations created in \ref custom_memory_pools.++    Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number,+    never read or accessed randomly, so a memory type can be selected that is uncached and write-combined.++    \warning Violating this declaration may work correctly, but will likely be very slow.+    Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;`+    Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once.+    */+    VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400,+    /**+    Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT).++    - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value,+      you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect.+    - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`.+      This includes allocations created in \ref custom_memory_pools.++    Declares that mapped memory can be read, written, and accessed in random order,+    so a `HOST_CACHED` memory type is preferred.+    */+    VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800,+    /**+    Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,+    it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected+    if it may improve performance.++    By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type+    (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and+    issue an explicit transfer to write/read your data.+    To prepare for this possibility, don't forget to add appropriate flags like+    `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image.+    */+    VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000,+    /** Allocation strategy that chooses smallest possible free range for the allocation+    to minimize memory usage and fragmentation, possibly at the expense of allocation time.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000,+    /** Allocation strategy that chooses first suitable free range for the allocation -+    not necessarily in terms of the smallest offset but the one that is easiest and fastest to find+    to minimize allocation time, possibly at the expense of allocation quality.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000,+    /** Allocation strategy that chooses always the lowest offset in available space.+    This is not the most efficient strategy but achieves highly packed data.+    Used internally by defragmentation, not recommended in typical usage.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT  = 0x00040000,+    /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,+    /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,+    /** A bit mask to extract only `STRATEGY` bits from entire set of flags.+    */+    VMA_ALLOCATION_CREATE_STRATEGY_MASK =+        VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT |+        VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT |+        VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,++    VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaAllocationCreateFlagBits;+/// See #VmaAllocationCreateFlagBits.+typedef VkFlags VmaAllocationCreateFlags;++/// Flags to be passed as VmaPoolCreateInfo::flags.+typedef enum VmaPoolCreateFlagBits+{+    /** \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 vmaCreateBuffer(), vmaCreateImage(),+    vmaAllocateMemoryForBuffer(), 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 vmaAllocateMemoryForImage() or vmaAllocateMemory(),+    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.+    */+    VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002,++    /** \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.+    */+    VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004,++    /** Bit mask to extract only `ALGORITHM` bits from entire set of flags.+    */+    VMA_POOL_CREATE_ALGORITHM_MASK =+        VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT,++    VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaPoolCreateFlagBits;+/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits.+typedef VkFlags VmaPoolCreateFlags;++/// Flags to be passed as VmaDefragmentationInfo::flags.+typedef enum VmaDefragmentationFlagBits+{+    /* \brief Use simple but fast algorithm for defragmentation.+    May not achieve best results but will require least time to compute and least allocations to copy.+    */+    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1,+    /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified.+    Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved.+    */+    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2,+    /* \brief Perform full defragmentation of memory.+    Can result in notably more time to compute and allocations to copy, but will achieve best memory packing.+    */+    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4,+    /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make.+    Only available when bufferImageGranularity is greater than 1, since it aims to reduce+    alignment issues between different types of resources.+    Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT.+    */+    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8,++    /// A bit mask to extract only `ALGORITHM` bits from entire set of flags.+    VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK =+        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT |+        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT |+        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT |+        VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT,++    VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaDefragmentationFlagBits;+/// See #VmaDefragmentationFlagBits.+typedef VkFlags VmaDefragmentationFlags;++/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove.+typedef enum VmaDefragmentationMoveOperation+{+    /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass().+    VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0,+    /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged.+    VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1,+    /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed.+    VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2,+} VmaDefragmentationMoveOperation;++/** @} */++/**+\addtogroup group_virtual+@{+*/++/// Flags to be passed as VmaVirtualBlockCreateInfo::flags.+typedef enum VmaVirtualBlockCreateFlagBits+{+    /** \brief Enables alternative, linear allocation algorithm in this virtual block.++    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.+    */+    VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001,++    /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags.+    */+    VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK =+        VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT,++    VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaVirtualBlockCreateFlagBits;+/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits.+typedef VkFlags VmaVirtualBlockCreateFlags;++/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags.+typedef enum VmaVirtualAllocationCreateFlagBits+{+    /** \brief Allocation will be created from upper stack in a double stack pool.++    This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag.+    */+    VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT,+    /** \brief Allocation strategy that tries to minimize memory usage.+    */+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT,+    /** \brief Allocation strategy that tries to minimize allocation time.+    */+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT,+    /** Allocation strategy that chooses always the lowest offset in available space.+    This is not the most efficient strategy but achieves highly packed data.+    */+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,+    /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags.++    These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits.+    */+    VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK,++    VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF+} VmaVirtualAllocationCreateFlagBits;+/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits.+typedef VkFlags VmaVirtualAllocationCreateFlags;++/** @} */++#endif // _VMA_ENUM_DECLARATIONS++#ifndef _VMA_DATA_TYPES_DECLARATIONS++/**+\addtogroup group_init+@{ */++/** \struct VmaAllocator+\brief Represents main object of this library initialized.++Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it.+Call function vmaDestroyAllocator() to destroy it.++It is recommended to create just one object of this type per `VkDevice` object,+right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed.+*/+VK_DEFINE_HANDLE(VmaAllocator)++/** @} */++/**+\addtogroup group_alloc+@{+*/++/** \struct VmaPool+\brief Represents custom memory pool++Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it.+Call function vmaDestroyPool() to destroy it.++For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools).+*/+VK_DEFINE_HANDLE(VmaPool)++/** \struct VmaAllocation+\brief Represents single memory allocation.++It may be either dedicated block of `VkDeviceMemory` 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 VmaAllocationCreateInfo.+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 vmaGetAllocationInfo() and inspect+returned structure VmaAllocationInfo.+*/+VK_DEFINE_HANDLE(VmaAllocation)++/** \struct VmaDefragmentationContext+\brief An opaque object that represents started defragmentation process.++Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it.+Call function vmaEndDefragmentation() to destroy it.+*/+VK_DEFINE_HANDLE(VmaDefragmentationContext)++/** @} */++/**+\addtogroup group_virtual+@{+*/++/** \struct VmaVirtualAllocation+\brief Represents single memory allocation done inside VmaVirtualBlock.++Use it as a unique identifier to virtual allocation within the single block.++Use value `VK_NULL_HANDLE` to represent a null/invalid allocation.+*/+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation)++/** @} */++/**+\addtogroup group_virtual+@{+*/++/** \struct VmaVirtualBlock+\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory.++Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it.+For more information, see documentation chapter \ref virtual_allocator.++This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally.+*/+VK_DEFINE_HANDLE(VmaVirtualBlock)++/** @} */++/**+\addtogroup group_init+@{+*/++/// Callback function called after successful vkAllocateMemory.+typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)(+    VmaAllocator VMA_NOT_NULL                    allocator,+    uint32_t                                     memoryType,+    VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,+    VkDeviceSize                                 size,+    void* VMA_NULLABLE                           pUserData);++/// Callback function called before vkFreeMemory.+typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)(+    VmaAllocator VMA_NOT_NULL                    allocator,+    uint32_t                                     memoryType,+    VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory,+    VkDeviceSize                                 size,+    void* VMA_NULLABLE                           pUserData);++/** \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 VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.+*/+typedef struct VmaDeviceMemoryCallbacks+{+    /// Optional, can be null.+    PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate;+    /// Optional, can be null.+    PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree;+    /// Optional, can be null.+    void* VMA_NULLABLE pUserData;+} VmaDeviceMemoryCallbacks;++/** \brief Pointers to some Vulkan functions - a subset used by the library.++Used in VmaAllocatorCreateInfo::pVulkanFunctions.+*/+typedef struct VmaVulkanFunctions+{+    /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.+    PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr;+    /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS.+    PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr;+    PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties;+    PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties;+    PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory;+    PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory;+    PFN_vkMapMemory VMA_NULLABLE vkMapMemory;+    PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory;+    PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges;+    PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges;+    PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory;+    PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory;+    PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements;+    PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements;+    PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer;+    PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer;+    PFN_vkCreateImage VMA_NULLABLE vkCreateImage;+    PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage;+    PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer;+#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.+    PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR;+    /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension.+    PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR;+#endif+#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000+    /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension.+    PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR;+    /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension.+    PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR;+#endif+#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000+    /// Fetch from "vkGetPhysicalDeviceMemoryProperties2" on Vulkan >= 1.1, but you can also fetch it from "vkGetPhysicalDeviceMemoryProperties2KHR" if you enabled extension VK_KHR_get_physical_device_properties2.+    PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR;+#endif+#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.+    PFN_vkGetDeviceBufferMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceBufferMemoryRequirements;+    /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4.+    PFN_vkGetDeviceImageMemoryRequirementsKHR VMA_NULLABLE vkGetDeviceImageMemoryRequirements;+#endif+#if VMA_EXTERNAL_MEMORY_WIN32+    PFN_vkGetMemoryWin32HandleKHR VMA_NULLABLE vkGetMemoryWin32HandleKHR;+#else+    void* VMA_NULLABLE vkGetMemoryWin32HandleKHR;+#endif+} VmaVulkanFunctions;++/// Description of a Allocator to be created.+typedef struct VmaAllocatorCreateInfo+{+    /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum.+    VmaAllocatorCreateFlags flags;+    /// Vulkan physical device.+    /** It must be valid throughout whole lifetime of created allocator. */+    VkPhysicalDevice VMA_NOT_NULL physicalDevice;+    /// Vulkan device.+    /** It must be valid throughout whole lifetime of created allocator. */+    VkDevice VMA_NOT_NULL device;+    /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional.+    /** Set to 0 to use default, which is currently 256 MiB. */+    VkDeviceSize preferredLargeHeapBlockSize;+    /// Custom CPU memory allocation callbacks. Optional.+    /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */+    const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;+    /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional.+    /** Optional, can be null. */+    const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks;+    /** \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+    `VkPhysicalDeviceMemoryProperties::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 `VK_ERROR_OUT_OF_DEVICE_MEMORY`.+    - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the+      value of this limit will be reported instead when using vmaGetMemoryProperties().++    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 `VK_ERROR_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.+    */+    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit;++    /** \brief Pointers to Vulkan functions. Can be null.++    For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions).+    */+    const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions;+    /** \brief Handle to Vulkan instance object.++    Starting from version 3.0.0 this member is no longer optional, it must be set!+    */+    VkInstance VMA_NOT_NULL instance;+    /** \brief Optional. Vulkan version that the application uses.++    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.+    Only versions 1.0...1.4 are supported by the current implementation.+    Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`.+    It must match the Vulkan version used by the application and supported on the selected physical device,+    so it must be no higher than `VkApplicationInfo::apiVersion` passed to `vkCreateInstance`+    and no higher than `VkPhysicalDeviceProperties::apiVersion` found on the physical device used.+    */+    uint32_t vulkanApiVersion;+#if VMA_EXTERNAL_MEMORY+    /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type.++    If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount`+    elements, defining external memory handle types of particular Vulkan memory type,+    to be passed using `VkExportMemoryAllocateInfoKHR`.++    Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type.+    This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL.+    */+    const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes;+#endif // #if VMA_EXTERNAL_MEMORY+} VmaAllocatorCreateInfo;++/// Information about existing #VmaAllocator object.+typedef struct VmaAllocatorInfo+{+    /** \brief Handle to Vulkan instance object.++    This is the same value as has been passed through VmaAllocatorCreateInfo::instance.+    */+    VkInstance VMA_NOT_NULL instance;+    /** \brief Handle to Vulkan physical device object.++    This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice.+    */+    VkPhysicalDevice VMA_NOT_NULL physicalDevice;+    /** \brief Handle to Vulkan device object.++    This is the same value as has been passed through VmaAllocatorCreateInfo::device.+    */+    VkDevice VMA_NOT_NULL device;+} VmaAllocatorInfo;++/** @} */++/**+\addtogroup group_stats+@{+*/++/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total.++These are fast to calculate.+See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics().+*/+typedef struct VmaStatistics+{+    /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated.+    */+    uint32_t blockCount;+    /** \brief Number of #VmaAllocation objects allocated.++    Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`.+    */+    uint32_t allocationCount;+    /** \brief Number of bytes allocated in `VkDeviceMemory` blocks.++    \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object+    (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls+    "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image.+    */+    VkDeviceSize blockBytes;+    /** \brief Total number of bytes occupied by all #VmaAllocation objects.++    Always less or equal than `blockBytes`.+    Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan+    but unused by any #VmaAllocation.+    */+    VkDeviceSize allocationBytes;+} VmaStatistics;++/** \brief More detailed statistics than #VmaStatistics.++These are slower to calculate. Use for debugging purposes.+See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics().++Previous version of the statistics API provided averages, but they have been removed+because they can be easily calculated as:++\code+VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount;+VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes;+VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount;+\endcode+*/+typedef struct VmaDetailedStatistics+{+    /// Basic statistics.+    VmaStatistics statistics;+    /// Number of free ranges of memory between allocations.+    uint32_t unusedRangeCount;+    /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations.+    VkDeviceSize allocationSizeMin;+    /// Largest allocation size. 0 if there are 0 allocations.+    VkDeviceSize allocationSizeMax;+    /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges.+    VkDeviceSize unusedRangeSizeMin;+    /// Largest empty range size. 0 if there are 0 empty ranges.+    VkDeviceSize unusedRangeSizeMax;+} VmaDetailedStatistics;++/** \brief  General statistics from current state of the Allocator -+total memory usage across all memory heaps and types.++These are slower to calculate. Use for debugging purposes.+See function vmaCalculateStatistics().+*/+typedef struct VmaTotalStatistics+{+    VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES];+    VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS];+    VmaDetailedStatistics total;+} VmaTotalStatistics;++/** \brief Statistics of current memory usage and available budget for a specific memory heap.++These are fast to calculate.+See function vmaGetHeapBudgets().+*/+typedef struct VmaBudget+{+    /** \brief Statistics fetched from the library.+    */+    VmaStatistics statistics;+    /** \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 `statistics.blockBytes` (usually higher) due to additional implicit objects+    also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or+    `VkDeviceMemory` blocks allocated outside of this library, if any.+    */+    VkDeviceSize usage;+    /** \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 `VkMemoryHeap::size[heapIndex]` due to factors+    external to the program, decided by the operating system.+    Difference `budget - usage` is the amount of additional memory that can probably+    be allocated without problems. Exceeding the budget may result in various problems.+    */+    VkDeviceSize budget;+} VmaBudget;++/** @} */++/**+\addtogroup group_alloc+@{+*/++/** \brief Parameters of new #VmaAllocation.++To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others.+*/+typedef struct VmaAllocationCreateInfo+{+    /// Use #VmaAllocationCreateFlagBits enum.+    VmaAllocationCreateFlags flags;+    /** \brief Intended usage of memory.++    You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n+    If `pool` is not null, this member is ignored.+    */+    VmaMemoryUsage usage;+    /** \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.*/+    VkMemoryPropertyFlags requiredFlags;+    /** \brief Flags that preferably should be set in a memory type chosen for an allocation.++    Set to 0 if no additional flags are preferred. \n+    If `pool` is not null, this member is ignored. */+    VkMemoryPropertyFlags preferredFlags;+    /** \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.+    */+    uint32_t memoryTypeBits;+    /** \brief Pool that this allocation should be created in.++    Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:+    `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.+    */+    VmaPool VMA_NULLABLE pool;+    /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().++    If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, 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.+    */+    void* VMA_NULLABLE pUserData;+    /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations.++    It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object+    and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.+    Otherwise, it has the priority of a memory block where it is placed and this variable is ignored.+    */+    float priority;+} VmaAllocationCreateInfo;++/// Describes parameter of created #VmaPool.+typedef struct VmaPoolCreateInfo+{+    /** \brief Vulkan memory type index to allocate this pool from.+    */+    uint32_t memoryTypeIndex;+    /** \brief Use combination of #VmaPoolCreateFlagBits.+    */+    VmaPoolCreateFlags flags;+    /** \brief Size of a single `VkDeviceMemory` 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.+    In this case, the pool will also support dedicated allocations.+    */+    VkDeviceSize blockSize;+    /** \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.+    */+    size_t minBlockCount;+    /** \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 VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated+    throughout whole lifetime of this pool.+    */+    size_t maxBlockCount;+    /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations.++    It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object.+    Otherwise, this variable is ignored.+    */+    float priority;+    /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0.++    Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two.+    It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough,+    e.g. when doing interop with OpenGL.+    */+    VkDeviceSize minAllocationAlignment;+    /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional.++    Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`.+    It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`.+    Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool.++    Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`,+    can be attached automatically by this library when using other, more convenient of its features.+    */+    void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkMemoryAllocateInfo) pMemoryAllocateNext;+} VmaPoolCreateInfo;++/** @} */++/**+\addtogroup group_alloc+@{+*/++/**+Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().++There is also an extended version of this structure that carries additional parameters: #VmaAllocationInfo2.+*/+typedef struct VmaAllocationInfo+{+    /** \brief Memory type index that this allocation was allocated from.++    It never changes.+    */+    uint32_t memoryType;+    /** \brief Handle to Vulkan memory object.++    Same memory object can be shared by multiple allocations.++    It can change after the allocation is moved during \ref defragmentation.+    */+    VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory;+    /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation.++    You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function+    vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image,+    not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation+    and apply this offset automatically.++    It can change after the allocation is moved during \ref defragmentation.+    */+    VkDeviceSize offset;+    /** \brief Size of this allocation, in bytes.++    It never changes.++    \note Allocation size returned in this variable may be greater than the size+    requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the+    allocation is accessible for operations on memory e.g. using a pointer after+    mapping with vmaMapMemory(), but operations on the resource e.g. using+    `vkCmdCopyBuffer` must be limited to the size of the resource.+    */+    VkDeviceSize size;+    /** \brief Pointer to the beginning of this allocation as mapped data.++    If the allocation hasn't been mapped using vmaMapMemory() and hasn't been+    created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null.++    It can change after call to vmaMapMemory(), vmaUnmapMemory().+    It can also change after the allocation is moved during \ref defragmentation.+    */+    void* VMA_NULLABLE pMappedData;+    /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData().++    It can change after call to vmaSetAllocationUserData() for this allocation.+    */+    void* VMA_NULLABLE pUserData;+    /** \brief Custom allocation name that was set with vmaSetAllocationName().++    It can change after call to vmaSetAllocationName() for this allocation.++    Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with+    additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED].+    */+    const char* VMA_NULLABLE pName;+} VmaAllocationInfo;++/// Extended parameters of a #VmaAllocation object that can be retrieved using function vmaGetAllocationInfo2().+typedef struct VmaAllocationInfo2+{+    /** \brief Basic parameters of the allocation.+    +    If you need only these, you can use function vmaGetAllocationInfo() and structure #VmaAllocationInfo instead.+    */+    VmaAllocationInfo allocationInfo;+    /** \brief Size of the `VkDeviceMemory` block that the allocation belongs to.+    +    In case of an allocation with dedicated memory, it will be equal to `allocationInfo.size`.+    */+    VkDeviceSize blockSize;+    /** \brief `VK_TRUE` if the allocation has dedicated memory, `VK_FALSE` if it was placed as part of a larger memory block.+    +    When `VK_TRUE`, it also means `VkMemoryDedicatedAllocateInfo` was used when creating the allocation+    (if VK_KHR_dedicated_allocation extension or Vulkan version >= 1.1 is enabled).+    */+    VkBool32 dedicatedMemory;+} VmaAllocationInfo2;++/** Callback function called during vmaBeginDefragmentation() to check custom criterion about ending current defragmentation pass.++Should return true if the defragmentation needs to stop current pass.+*/+typedef VkBool32 (VKAPI_PTR* PFN_vmaCheckDefragmentationBreakFunction)(void* VMA_NULLABLE pUserData);++/** \brief Parameters for defragmentation.++To be used with function vmaBeginDefragmentation().+*/+typedef struct VmaDefragmentationInfo+{+    /// \brief Use combination of #VmaDefragmentationFlagBits.+    VmaDefragmentationFlags flags;+    /** \brief Custom pool to be defragmented.++    If null then default pools will undergo defragmentation process.+    */+    VmaPool VMA_NULLABLE pool;+    /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places.++    `0` means no limit.+    */+    VkDeviceSize maxBytesPerPass;+    /** \brief Maximum number of allocations that can be moved during single pass to a different place.++    `0` means no limit.+    */+    uint32_t maxAllocationsPerPass;+    /** \brief Optional custom callback for stopping vmaBeginDefragmentation().++    Have to return true for breaking current defragmentation pass.+    */+    PFN_vmaCheckDefragmentationBreakFunction VMA_NULLABLE pfnBreakCallback;+    /// \brief Optional data to pass to custom callback for stopping pass of defragmentation.+    void* VMA_NULLABLE pBreakCallbackUserData;+} VmaDefragmentationInfo;++/// Single move of an allocation to be done for defragmentation.+typedef struct VmaDefragmentationMove+{+    /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it.+    VmaDefragmentationMoveOperation operation;+    /// Allocation that should be moved.+    VmaAllocation VMA_NOT_NULL srcAllocation;+    /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`.++    \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass,+    to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory().+    vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory.+    */+    VmaAllocation VMA_NOT_NULL dstTmpAllocation;+} VmaDefragmentationMove;++/** \brief Parameters for incremental defragmentation steps.++To be used with function vmaBeginDefragmentationPass().+*/+typedef struct VmaDefragmentationPassMoveInfo+{+    /// Number of elements in the `pMoves` array.+    uint32_t moveCount;+    /** \brief Array of moves to be performed by the user in the current defragmentation pass.++    Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass().++    For each element, you should:++    1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset.+    2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`.+    3. Make sure these commands finished executing on the GPU.+    4. Destroy the old buffer/image.++    Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass().+    After this call, the allocation will point to the new place in memory.++    Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.++    Alternatively, if you decide you want to completely remove the allocation:++    1. Destroy its buffer/image.+    2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.++    Then, after vmaEndDefragmentationPass() the allocation will be freed.+    */+    VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves;+} VmaDefragmentationPassMoveInfo;++/// Statistics returned for defragmentation process in function vmaEndDefragmentation().+typedef struct VmaDefragmentationStats+{+    /// Total number of bytes that have been copied while moving allocations to different places.+    VkDeviceSize bytesMoved;+    /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects.+    VkDeviceSize bytesFreed;+    /// Number of allocations that have been moved to different places.+    uint32_t allocationsMoved;+    /// Number of empty `VkDeviceMemory` objects that have been released to the system.+    uint32_t deviceMemoryBlocksFreed;+} VmaDefragmentationStats;++/** @} */++/**+\addtogroup group_virtual+@{+*/++/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock().+typedef struct VmaVirtualBlockCreateInfo+{+    /** \brief Total size of the virtual block.++    Sizes can be expressed in bytes or any units you want as long as you are consistent in using them.+    For example, if you allocate from some array of structures, 1 can mean single instance of entire structure.+    */+    VkDeviceSize size;++    /** \brief Use combination of #VmaVirtualBlockCreateFlagBits.+    */+    VmaVirtualBlockCreateFlags flags;++    /** \brief Custom CPU memory allocation callbacks. Optional.++    Optional, can be null. When specified, they will be used for all CPU-side memory allocations.+    */+    const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks;+} VmaVirtualBlockCreateInfo;++/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate().+typedef struct VmaVirtualAllocationCreateInfo+{+    /** \brief Size of the allocation.++    Cannot be zero.+    */+    VkDeviceSize size;+    /** \brief Required alignment of the allocation. Optional.++    Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset.+    */+    VkDeviceSize alignment;+    /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits.+    */+    VmaVirtualAllocationCreateFlags flags;+    /** \brief Custom pointer to be associated with the allocation. Optional.++    It can be any value and can be used for user-defined purposes. It can be fetched or changed later.+    */+    void* VMA_NULLABLE pUserData;+} VmaVirtualAllocationCreateInfo;++/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo().+typedef struct VmaVirtualAllocationInfo+{+    /** \brief Offset of the allocation.++    Offset at which the allocation was made.+    */+    VkDeviceSize offset;+    /** \brief Size of the allocation.++    Same value as passed in VmaVirtualAllocationCreateInfo::size.+    */+    VkDeviceSize size;+    /** \brief Custom pointer associated with the allocation.++    Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData().+    */+    void* VMA_NULLABLE pUserData;+} VmaVirtualAllocationInfo;++/** @} */++#endif // _VMA_DATA_TYPES_DECLARATIONS++#ifndef _VMA_FUNCTION_HEADERS++/**+\addtogroup group_init+@{+*/++#ifdef VOLK_HEADER_VERSION+/** \brief Fully initializes `pDstVulkanFunctions` structure with Vulkan functions needed by VMA+using [volk library](https://github.com/zeux/volk).++This function is defined in VMA header only if "volk.h" was included before it.++To use this function properly:++-# Initialize volk and Vulkan:+   -# Call `volkInitialize()`+   -# Create `VkInstance` object+   -# Call `volkLoadInstance()`+   -# Create `VkDevice` object+   -# Call `volkLoadDevice()`+-# Fill in structure #VmaAllocatorCreateInfo, especially members:+   - VmaAllocatorCreateInfo::device+   - VmaAllocatorCreateInfo::vulkanApiVersion+   - VmaAllocatorCreateInfo::flags - set appropriate flags for the Vulkan extensions you enabled+-# Create an instance of the #VmaVulkanFunctions structure.+-# Call vmaImportVulkanFunctionsFromVolk().+   Parameter `pAllocatorCreateInfo` is read to find out which functions should be fetched for+   appropriate Vulkan version and extensions.+   Parameter `pDstVulkanFunctions` is filled with those function pointers, or null if not applicable.+-# Attach the #VmaVulkanFunctions structure to VmaAllocatorCreateInfo::pVulkanFunctions.+-# Call vmaCreateAllocator() to create the #VmaAllocator object.++Example:++\code+VmaAllocatorCreateInfo allocatorCreateInfo = {};+allocatorCreateInfo.physicalDevice = myPhysicalDevice;+allocatorCreateInfo.device = myDevice;+allocatorCreateInfo.instance = myInstance;+allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_3;+allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT |+    VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT |+    VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT;++VmaVulkanFunctions vulkanFunctions;+VkResult res = vmaImportVulkanFunctionsFromVolk(&allocatorCreateInfo, &vulkanFunctions);+// Check res...+allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;++VmaAllocator allocator;+res = vmaCreateAllocator(&allocatorCreateInfo, &allocator);+// Check res...+\endcode++Internally in this function, pointers to functions related to the entire Vulkan instance are fetched using global function definitions,+while pointers to functions related to the Vulkan device are fetched using `volkLoadDeviceTable()` for given `pAllocatorCreateInfo->device`.+ */+VMA_CALL_PRE VkResult VMA_CALL_POST vmaImportVulkanFunctionsFromVolk(+    const VmaAllocatorCreateInfo* VMA_NOT_NULL pAllocatorCreateInfo,+    VmaVulkanFunctions* VMA_NOT_NULL pDstVulkanFunctions);+#endif++/// Creates #VmaAllocator object.+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(+    const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator);++/// Destroys allocator object.+VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(+    VmaAllocator VMA_NULLABLE allocator);++/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc.++It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to+`VkPhysicalDevice`, `VkDevice` etc. every time using this function.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo);++/**+PhysicalDeviceProperties are fetched from physicalDevice by the allocator.+You can access it here, without fetching it again on your own.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties);++/**+PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator.+You can access it here, without fetching it again on your own.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties);++/**+\brief Given Memory Type Index, returns Property Flags of this memory type.++This is just a convenience function. Same information can be obtained using+vmaGetMemoryProperties().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t memoryTypeIndex,+    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);++/** \brief Sets index of the current frame.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t frameIndex);++/** @} */++/**+\addtogroup group_stats+@{+*/++/** \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. Use it for debugging purposes.+For faster but more brief statistics suitable to be called every frame or every allocation,+use vmaGetHeapBudgets().++Note that when using allocator from multiple threads, returned information may immediately+become outdated.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaTotalStatistics* VMA_NOT_NULL pStats);++/** \brief Retrieves information about current memory usage and budget for all memory heaps.++\param allocator+\param[out] pBudgets 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 vmaCalculateStatistics().++Note that when using allocator from multiple threads, returned information may immediately+become outdated.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets);++/** @} */++/**+\addtogroup group_alloc+@{+*/++/**+\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo.++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 VK_ERROR_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.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t memoryTypeBits,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);++/**+\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo.++It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.+It internally creates a temporary, dummy buffer that never has memory bound.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);++/**+\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo.++It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex.+It internally creates a temporary, dummy image that never has memory bound.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    uint32_t* VMA_NOT_NULL pMemoryTypeIndex);++/** \brief Allocates Vulkan device memory and creates #VmaPool object.++\param allocator Allocator object.+\param pCreateInfo Parameters of pool to create.+\param[out] pPool Handle to created pool.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(+    VmaAllocator VMA_NOT_NULL allocator,+    const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool);++/** \brief Destroys #VmaPool object and frees Vulkan device memory.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NULLABLE pool);++/** @} */++/**+\addtogroup group_stats+@{+*/++/** \brief Retrieves statistics of existing #VmaPool object.++\param allocator Allocator object.+\param pool Pool object.+\param[out] pPoolStats Statistics of specified pool.++Note that when using the pool from multiple threads, returned information may immediately+become outdated.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NOT_NULL pool,+    VmaStatistics* VMA_NOT_NULL pPoolStats);++/** \brief Retrieves detailed statistics of existing #VmaPool object.++\param allocator Allocator object.+\param pool Pool object.+\param[out] pPoolStats Statistics of specified pool.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NOT_NULL pool,+    VmaDetailedStatistics* VMA_NOT_NULL pPoolStats);++/** @} */++/**+\addtogroup group_alloc+@{+*/++/** \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:++- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool.+- `VK_SUCCESS` - corruption detection has been performed and succeeded.+- `VK_ERROR_UNKNOWN` - 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.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NOT_NULL pool);++/** \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 vmaSetPoolName().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NOT_NULL pool,+    const char* VMA_NULLABLE* VMA_NOT_NULL ppName);++/** \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.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaPool VMA_NOT_NULL pool,+    const char* VMA_NULLABLE pName);++/** \brief General purpose memory allocation.++\param allocator+\param pVkMemoryRequirements+\param pCreateInfo+\param[out] pAllocation Handle to allocated memory.+\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().++You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages().++It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(),+vmaCreateBuffer(), vmaCreateImage() instead whenever possible.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/** \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 allocation.+\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 vmaFreeMemory() or vmaFreeMemoryPages().++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 vmaAllocateMemory() `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 `VK_NULL_HANDLE`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements,+    const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo,+    size_t allocationCount,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations,+    VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo);++/** \brief Allocates memory suitable for given `VkBuffer`.++\param allocator+\param buffer+\param pCreateInfo+\param[out] pAllocation Handle to allocated memory.+\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().++It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory().++This is a special-purpose function. In most cases you should use vmaCreateBuffer().++You must free the allocation using vmaFreeMemory() when no longer needed.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(+    VmaAllocator VMA_NOT_NULL allocator,+    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/** \brief Allocates memory suitable for given `VkImage`.++\param allocator+\param image+\param pCreateInfo+\param[out] pAllocation Handle to allocated memory.+\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo().++It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory().++This is a special-purpose function. In most cases you should use vmaCreateImage().++You must free the allocation using vmaFreeMemory() when no longer needed.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(+    VmaAllocator VMA_NOT_NULL allocator,+    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().++Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NULLABLE allocation);++/** \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. vmaAllocateMemory(),+vmaAllocateMemoryPages() and other functions.+It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times.++Allocations in `pAllocations` array can come from any memory pools and types.+Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(+    VmaAllocator VMA_NOT_NULL allocator,+    size_t allocationCount,+    const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations);++/** \brief Returns current information about specified allocation.++Current parameters of given allocation are returned in `pAllocationInfo`.++Although this function doesn't lock any mutex, so it should be quite efficient,+you should avoid calling it too often.+You can retrieve same VmaAllocationInfo structure while creating your resource, from function+vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change+(e.g. due to defragmentation).++There is also a new function vmaGetAllocationInfo2() that offers extended information+about the allocation, returned using new structure #VmaAllocationInfo2.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo);++/** \brief Returns extended information about specified allocation.++Current parameters of given allocation are returned in `pAllocationInfo`.+Extended parameters in structure #VmaAllocationInfo2 include memory block size+and a flag telling whether the allocation has dedicated memory.+It can be useful e.g. for interop with OpenGL.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VmaAllocationInfo2* VMA_NOT_NULL pAllocationInfo);++/** \brief Sets pUserData in given allocation to new value.++The value of pointer `pUserData` is 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.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    void* VMA_NULLABLE pUserData);++/** \brief Sets pName in given allocation to new value.++`pName` 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 `pName`. String+passed as pName 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+`pName` is freed from memory.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const char* VMA_NULLABLE pName);++/**+\brief Given an allocation, returns Property Flags of its memory type.++This is just a convenience function. Same information can be obtained using+vmaGetAllocationInfo() + vmaGetMemoryProperties().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags);+++#if VMA_EXTERNAL_MEMORY_WIN32+/**+\brief Given an allocation, returns Win32 handle that may be imported by other processes or APIs.++\param hTargetProcess Must be a valid handle to target process or null. If it's null, the function returns+    handle for the current process.+\param[out] pHandle Output parameter that returns the handle.++The function fills `pHandle` with handle that can be used in target process.+The handle is fetched using function `vkGetMemoryWin32HandleKHR`.+When no longer needed, you must close it using:++\code+CloseHandle(handle);+\endcode++You can close it any time, before or after destroying the allocation object.+It is reference-counted internally by Windows.++Note the handle is returned for the entire `VkDeviceMemory` block that the allocation belongs to.+If the allocation is sub-allocated from a larger block, you may need to consider the offset of the allocation+(VmaAllocationInfo::offset).++If the function fails with `VK_ERROR_FEATURE_NOT_PRESENT` error code, please double-check+that VmaVulkanFunctions::vkGetMemoryWin32HandleKHR function pointer is set, e.g. either by using `VMA_DYNAMIC_VULKAN_FUNCTIONS`+or by manually passing it through VmaAllocatorCreateInfo::pVulkanFunctions.++For more information, see chapter \ref vk_khr_external_memory_win32.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle);+#endif // VMA_EXTERNAL_MEMORY_WIN32++/** \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.++\warning+If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is+correctly offsetted to the beginning of region assigned to this particular allocation.+Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block.+You should not add VmaAllocationInfo::offset to it!++Mapping is internally reference-counted and synchronized, so despite raw Vulkan+function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory`+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 vmaUnmapMemory() 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 vmaUnmapMemory() same number of times as you called vmaMapMemory().++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 vmaUnmapMemory() same number of times as you called+vmaMapMemory(). You must not call vmaUnmapMemory() 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 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 vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    void* VMA_NULLABLE* VMA_NOT_NULL ppData);++/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory().++For details, see description of vmaMapMemory().++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 vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation);++/** \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`!!!++This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is+called, otherwise `VK_SUCCESS`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize offset,+    VkDeviceSize size);++/** \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`!!!++This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if+it is called, otherwise `VK_SUCCESS`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize offset,+    VkDeviceSize size);++/** \brief Flushes memory of given set of allocations.++Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations.+For more information, see documentation of vmaFlushAllocation().++\param allocator+\param allocationCount+\param allocations+\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all offsets are zero.+\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.++This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is+called, otherwise `VK_SUCCESS`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t allocationCount,+    const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,+    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,+    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);++/** \brief Invalidates memory of given set of allocations.++Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations.+For more information, see documentation of vmaInvalidateAllocation().++\param allocator+\param allocationCount+\param allocations+\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all offsets are zero.+\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations.++This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is+called, otherwise `VK_SUCCESS`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t allocationCount,+    const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations,+    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,+    const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);++/** \brief Maps the allocation temporarily if needed, copies data from specified host pointer to it, and flushes the memory from the host caches if needed.++\param allocator+\param pSrcHostPointer Pointer to the host data that become source of the copy.+\param dstAllocation   Handle to the allocation that becomes destination of the copy.+\param dstAllocationLocalOffset  Offset within `dstAllocation` where to write copied data, in bytes.+\param size            Number of bytes to copy.++This is a convenience function that allows to copy data from a host pointer to an allocation easily.+Same behavior can be achieved by calling vmaMapMemory(), `memcpy()`, vmaUnmapMemory(), vmaFlushAllocation().++This function can be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.+It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or+#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.+Otherwise, the function will fail and generate a Validation Layers error.++`dstAllocationLocalOffset` is relative to the contents of given `dstAllocation`.+If you mean whole allocation, you should pass 0.+Do not pass allocation's offset within device memory block this parameter!+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(+    VmaAllocator VMA_NOT_NULL allocator,+    const void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pSrcHostPointer,+    VmaAllocation VMA_NOT_NULL dstAllocation,+    VkDeviceSize dstAllocationLocalOffset,+    VkDeviceSize size);++/** \brief Invalidates memory in the host caches if needed, maps the allocation temporarily if needed, and copies data from it to a specified host pointer.++\param allocator+\param srcAllocation   Handle to the allocation that becomes source of the copy.+\param srcAllocationLocalOffset  Offset within `srcAllocation` where to read copied data, in bytes.+\param pDstHostPointer Pointer to the host memory that become destination of the copy.+\param size            Number of bytes to copy.++This is a convenience function that allows to copy data from an allocation to a host pointer easily.+Same behavior can be achieved by calling vmaInvalidateAllocation(), vmaMapMemory(), `memcpy()`, vmaUnmapMemory().++This function should be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`+and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT` flag.+It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.+Otherwise, the function may fail and generate a Validation Layers error.+It may also work very slowly when reading from an uncached memory.++`srcAllocationLocalOffset` is relative to the contents of given `srcAllocation`.+If you mean whole allocation, you should pass 0.+Do not pass allocation's offset within device memory block as this parameter!+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL srcAllocation,+    VkDeviceSize srcAllocationLocalOffset,+    void* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(size) pDstHostPointer,+    VkDeviceSize size);++/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.++\param allocator+\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:++- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types.+- `VK_SUCCESS` - corruption detection has been performed and succeeded.+- `VK_ERROR_UNKNOWN` - 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.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(+    VmaAllocator VMA_NOT_NULL allocator,+    uint32_t memoryTypeBits);++/** \brief Begins defragmentation process.++\param allocator Allocator object.+\param pInfo Structure filled with parameters of defragmentation.+\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation.+\returns+- `VK_SUCCESS` if defragmentation can begin.+- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.++For more information about defragmentation, see documentation chapter:+[Defragmentation](@ref defragmentation).+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(+    VmaAllocator VMA_NOT_NULL allocator,+    const VmaDefragmentationInfo* VMA_NOT_NULL pInfo,+    VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext);++/** \brief Ends defragmentation process.++\param allocator Allocator object.+\param context Context object that has been created by vmaBeginDefragmentation().+\param[out] pStats Optional stats for the defragmentation. Can be null.++Use this function to finish defragmentation started by vmaBeginDefragmentation().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaDefragmentationContext VMA_NOT_NULL context,+    VmaDefragmentationStats* VMA_NULLABLE pStats);++/** \brief Starts single defragmentation pass.++\param allocator Allocator object.+\param context Context object that has been created by vmaBeginDefragmentation().+\param[out] pPassInfo Computed information for current pass.+\returns+- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation.+- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(),+  and then preferably try another pass with vmaBeginDefragmentationPass().+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaDefragmentationContext VMA_NOT_NULL context,+    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);++/** \brief Ends single defragmentation pass.++\param allocator Allocator object.+\param context Context object that has been created by vmaBeginDefragmentation().+\param pPassInfo Computed information for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you.++Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible.++Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`.+After this call:++- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY+  (which is the default) will be pointing to the new destination place.+- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY+  will be freed.++If no more moves are possible you can end whole defragmentation.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaDefragmentationContext VMA_NOT_NULL context,+    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo);++/** \brief Binds buffer to allocation.++Binds specified buffer to region of memory represented by specified allocation.+Gets `VkDeviceMemory` 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 `VkDeviceMemory` 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 vmaCreateBuffer() instead of this one.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer);++/** \brief Binds buffer to allocation with additional parameters.++\param allocator+\param allocation+\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.+\param buffer+\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null.++This function is similar to vmaBindBufferMemory(), but it provides additional parameters.++If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag+or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer,+    const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindBufferMemoryInfoKHR) pNext);++/** \brief Binds image to allocation.++Binds specified image to region of memory represented by specified allocation.+Gets `VkDeviceMemory` 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 `VkDeviceMemory` 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 vmaCreateImage() instead of this one.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image);++/** \brief Binds image to allocation with additional parameters.++\param allocator+\param allocation+\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0.+\param image+\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null.++This function is similar to vmaBindImageMemory(), but it provides additional parameters.++If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag+or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    VkImage VMA_NOT_NULL_NON_DISPATCHABLE image,+    const void* VMA_NULLABLE VMA_EXTENDS_VK_STRUCT(VkBindImageMemoryInfoKHR) pNext);++/** \brief Creates a new `VkBuffer`, allocates and binds memory for it.++\param allocator+\param pBufferCreateInfo+\param pAllocationCreateInfo+\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 vmaGetAllocationInfo().++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 vmaDestroyBuffer() or+separately, using `vkDestroyBuffer()` and vmaFreeMemory().++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+(#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.++\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer,+although recommended as a good practice, is out of scope of this library and could be implemented+by the user as a higher-level logic on top of VMA.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/** \brief Creates a buffer with additional minimum alignment.++Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom,+minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g.+for interop with OpenGL.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    VkDeviceSize minAlignment,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/** \brief Creates a new `VkBuffer`, binds already created memory for it.++\param allocator+\param allocation Allocation that provides memory to be used for binding new buffer to it.+\param pBufferCreateInfo+\param[out] pBuffer Buffer that was created.++This function automatically:++-# Creates buffer.+-# Binds the buffer with the supplied memory.++If any of these operations fail, buffer is not created,+returned value is negative error code and `*pBuffer` is null.++If the function succeeded, you must destroy the buffer when you+no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding+allocation you can use convenience function vmaDestroyBuffer().++\note There is a new version of this function augmented with parameter `allocationLocalOffset` - see vmaCreateAliasingBuffer2().+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);++/** \brief Creates a new `VkBuffer`, binds already created memory for it.++\param allocator+\param allocation Allocation that provides memory to be used for binding new buffer to it.+\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the allocation. Normally it should be 0.+\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created.++This function automatically:++-# Creates buffer.+-# Binds the buffer with the supplied memory.++If any of these operations fail, buffer is not created,+returned value is negative error code and `*pBuffer` is null.++If the function succeeded, you must destroy the buffer when you+no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding+allocation you can use convenience function vmaDestroyBuffer().++\note This is a new version of the function augmented with parameter `allocationLocalOffset`.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer);++/** \brief Destroys Vulkan buffer and frees allocated memory.++This is just a convenience function equivalent to:++\code+vkDestroyBuffer(device, buffer, allocationCallbacks);+vmaFreeMemory(allocator, allocation);+\endcode++It is safe to pass null as buffer and/or allocation.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(+    VmaAllocator VMA_NOT_NULL allocator,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer,+    VmaAllocation VMA_NULLABLE allocation);++/// Function similar to vmaCreateBuffer().+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(+    VmaAllocator VMA_NOT_NULL allocator,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage,+    VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation,+    VmaAllocationInfo* VMA_NULLABLE pAllocationInfo);++/// Function similar to vmaCreateAliasingBuffer() but for images.+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);++/// Function similar to vmaCreateAliasingBuffer2() but for images.+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage);++/** \brief Destroys Vulkan image and frees allocated memory.++This is just a convenience function equivalent to:++\code+vkDestroyImage(device, image, allocationCallbacks);+vmaFreeMemory(allocator, allocation);+\endcode++It is safe to pass null as image and/or allocation.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(+    VmaAllocator VMA_NOT_NULL allocator,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE image,+    VmaAllocation VMA_NULLABLE allocation);++/** @} */++/**+\addtogroup group_virtual+@{+*/++/** \brief Creates new #VmaVirtualBlock object.++\param pCreateInfo Parameters for creation.+\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(+    const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock);++/** \brief Destroys #VmaVirtualBlock object.++Please note that you should consciously handle virtual allocations that could remain unfreed in the block.+You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock()+if you are sure this is what you want. If you do neither, an assert is called.++If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`,+don't forget to free them.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(+    VmaVirtualBlock VMA_NULLABLE virtualBlock);++/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations.+*/+VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock);++/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo);++/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock.++If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned+(despite the function doesn't ever allocate actual GPU memory).+`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`.++\param virtualBlock Virtual block+\param pCreateInfo Parameters for the allocation+\param[out] pAllocation Returned handle of the new allocation+\param[out] pOffset Returned offset of the new allocation. Optional, can be null.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,+    VkDeviceSize* VMA_NULLABLE pOffset);++/** \brief Frees virtual allocation inside given #VmaVirtualBlock.++It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation);++/** \brief Frees all virtual allocations inside given #VmaVirtualBlock.++You must either call this function or free each virtual allocation individually with vmaVirtualFree()+before destroying a virtual block. Otherwise, an assert is called.++If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`,+don't forget to free it as well.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock);++/** \brief Changes custom pointer associated with given virtual allocation.+*/+VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation,+    void* VMA_NULLABLE pUserData);++/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock.++This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaStatistics* VMA_NOT_NULL pStats);++/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock.++This function is slow to call. Use for debugging purposes.+For less detailed statistics, see vmaGetVirtualBlockStatistics().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaDetailedStatistics* VMA_NOT_NULL pStats);++/** @} */++#if VMA_STATS_STRING_ENABLED+/**+\addtogroup group_stats+@{+*/++/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock.+\param virtualBlock Virtual block.+\param[out] ppStatsString Returned string.+\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces.++Returned string must be freed using vmaFreeVirtualBlockStatsString().+*/+VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,+    VkBool32 detailedMap);++/// Frees a string returned by vmaBuildVirtualBlockStatsString().+VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(+    VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    char* VMA_NULLABLE pStatsString);++/** \brief Builds and returns statistics as a null-terminated string in JSON format.+\param allocator+\param[out] ppStatsString Must be freed using vmaFreeStatsString() function.+\param detailedMap+*/+VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(+    VmaAllocator VMA_NOT_NULL allocator,+    char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString,+    VkBool32 detailedMap);++VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(+    VmaAllocator VMA_NOT_NULL allocator,+    char* VMA_NULLABLE pStatsString);++/** @} */++#endif // VMA_STATS_STRING_ENABLED++#endif // _VMA_FUNCTION_HEADERS++#ifdef __cplusplus+}+#endif++#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H++////////////////////////////////////////////////////////////////////////////////+////////////////////////////////////////////////////////////////////////////////+//+//    IMPLEMENTATION+//+////////////////////////////////////////////////////////////////////////////////+////////////////////////////////////////////////////////////////////////////////++// For Visual Studio IntelliSense.+#if defined(__cplusplus) && defined(__INTELLISENSE__)+#define VMA_IMPLEMENTATION+#endif++#ifdef VMA_IMPLEMENTATION+#undef VMA_IMPLEMENTATION++#include <cstdint>+#include <cstdlib>+#include <cstring>+#include <cinttypes>+#include <utility>+#include <type_traits>++#if !defined(VMA_CPP20)+    #if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20+        #define VMA_CPP20 1+    #else+        #define VMA_CPP20 0+    #endif+#endif++#ifdef _MSC_VER+    #include <intrin.h> // For functions like __popcnt, _BitScanForward etc.+#endif+#if VMA_CPP20+    #include <bit>+#endif++#if VMA_STATS_STRING_ENABLED+    #include <cstdio> // For snprintf+#endif++/*******************************************************************************+CONFIGURATION SECTION++Define some of these macros before each #include of this header or change them+here if you need other then default behavior depending on your environment.+*/+#ifndef _VMA_CONFIGURATION++/*+Define this macro to 1 to make the library fetch pointers to Vulkan functions+internally, like:++    vulkanFunctions.vkAllocateMemory = &vkAllocateMemory;+*/+#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES)+    #define VMA_STATIC_VULKAN_FUNCTIONS 1+#endif++/*+Define this macro to 1 to make the library fetch pointers to Vulkan functions+internally, like:++    vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory");++To use this feature in new versions of VMA you now have to pass+VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as+VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null.+*/+#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS)+    #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1+#endif++#ifndef VMA_USE_STL_SHARED_MUTEX+    #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17+        #define VMA_USE_STL_SHARED_MUTEX 1+    // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus+    // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2.+    #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L+        #define VMA_USE_STL_SHARED_MUTEX 1+    #else+        #define VMA_USE_STL_SHARED_MUTEX 0+    #endif+#endif++/*+Define this macro to include custom header files without having to edit this file directly, e.g.:++    // Inside of "my_vma_configuration_user_includes.h":++    #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT+    #include "my_custom_min.h" // for my_custom_min+    #include <algorithm>+    #include <mutex>++    // Inside a different file, which includes "vk_mem_alloc.h":++    #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h"+    #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr)+    #define VMA_MIN(v1, v2)  (my_custom_min(v1, v2))+    #include "vk_mem_alloc.h"+    ...++The following headers are used in this CONFIGURATION section only, so feel free to+remove them if not needed.+*/+#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H)+    #include <cassert> // for assert+    #include <algorithm> // for min, max, swap+    #include <mutex>+#else+    #include VMA_CONFIGURATION_USER_INCLUDES_H+#endif++#ifndef VMA_NULL+   // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.+   #define VMA_NULL   nullptr+#endif++#ifndef VMA_FALLTHROUGH+    #if __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17+        #define VMA_FALLTHROUGH [[fallthrough]]+    #else+        #define VMA_FALLTHROUGH+    #endif+#endif++// Normal assert to check for programmer's errors, especially in Debug configuration.+#ifndef VMA_ASSERT+   #ifdef NDEBUG+       #define VMA_ASSERT(expr)+   #else+       #define VMA_ASSERT(expr)         assert(expr)+   #endif+#endif++// Assert that will be called very often, like inside data structures e.g. operator[].+// Making it non-empty can make program slow.+#ifndef VMA_HEAVY_ASSERT+   #ifdef NDEBUG+       #define VMA_HEAVY_ASSERT(expr)+   #else+       #define VMA_HEAVY_ASSERT(expr)   //VMA_ASSERT(expr)+   #endif+#endif++// Assert used for reporting memory leaks - unfreed allocations.+#ifndef VMA_ASSERT_LEAK+    #define VMA_ASSERT_LEAK(expr)   VMA_ASSERT(expr)+#endif++// If your compiler is not compatible with C++17 and definition of+// aligned_alloc() function is missing, uncommenting following line may help:++//#include <malloc.h>++#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)+#include <cstdlib>+static void* vma_aligned_alloc(size_t alignment, size_t size)+{+    // alignment must be >= sizeof(void*)+    if(alignment < sizeof(void*))+    {+        alignment = sizeof(void*);+    }++    return memalign(alignment, size);+}+#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC))+#include <cstdlib>++#if defined(__APPLE__)+#include <AvailabilityMacros.h>+#endif++static void* vma_aligned_alloc(size_t alignment, size_t size)+{+    // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4)+    // Therefore, for now disable this specific exception until a proper solution is found.+    //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0))+    //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0+    //    // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only+    //    // with the MacOSX11.0 SDK in Xcode 12 (which is what adds+    //    // MAC_OS_X_VERSION_10_16), even though the function is marked+    //    // available for 10.15. That is why the preprocessor checks for 10.16 but+    //    // the __builtin_available checks for 10.15.+    //    // People who use C++17 could call aligned_alloc with the 10.15 SDK already.+    //    if (__builtin_available(macOS 10.15, iOS 13, *))+    //        return aligned_alloc(alignment, size);+    //#endif+    //#endif++    // alignment must be >= sizeof(void*)+    if(alignment < sizeof(void*))+    {+        alignment = sizeof(void*);+    }++    void *pointer;+    if(posix_memalign(&pointer, alignment, size) == 0)+        return pointer;+    return VMA_NULL;+}+#elif defined(_WIN32)+static void* vma_aligned_alloc(size_t alignment, size_t size)+{+    return _aligned_malloc(size, alignment);+}+#elif __cplusplus >= 201703L || _MSVC_LANG >= 201703L // C++17+static void* vma_aligned_alloc(size_t alignment, size_t size)+{+    return aligned_alloc(alignment, size);+}+#else+static void* vma_aligned_alloc(size_t alignment, size_t size)+{+    VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system.");+    return VMA_NULL;+}+#endif++#if defined(_WIN32)+static void vma_aligned_free(void* ptr)+{+    _aligned_free(ptr);+}+#else+static void vma_aligned_free(void* VMA_NULLABLE ptr)+{+    free(ptr);+}+#endif++#ifndef VMA_ALIGN_OF+   #define VMA_ALIGN_OF(type)       (alignof(type))+#endif++#ifndef VMA_SYSTEM_ALIGNED_MALLOC+   #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size))+#endif++#ifndef VMA_SYSTEM_ALIGNED_FREE+   // VMA_SYSTEM_FREE is the old name, but might have been defined by the user+   #if defined(VMA_SYSTEM_FREE)+      #define VMA_SYSTEM_ALIGNED_FREE(ptr)     VMA_SYSTEM_FREE(ptr)+   #else+      #define VMA_SYSTEM_ALIGNED_FREE(ptr)     vma_aligned_free(ptr)+    #endif+#endif++#ifndef VMA_COUNT_BITS_SET+    // Returns number of bits set to 1 in (v)+    #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v)+#endif++#ifndef VMA_BITSCAN_LSB+    // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX+    #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask)+#endif++#ifndef VMA_BITSCAN_MSB+    // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX+    #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask)+#endif++#ifndef VMA_MIN+   #define VMA_MIN(v1, v2)    ((std::min)((v1), (v2)))+#endif++#ifndef VMA_MAX+   #define VMA_MAX(v1, v2)    ((std::max)((v1), (v2)))+#endif++#ifndef VMA_SORT+   #define VMA_SORT(beg, end, cmp)  std::sort(beg, end, cmp)+#endif++#ifndef VMA_DEBUG_LOG_FORMAT+   #define VMA_DEBUG_LOG_FORMAT(format, ...)+   /*+   #define VMA_DEBUG_LOG_FORMAT(format, ...) do { \+       printf((format), __VA_ARGS__); \+       printf("\n"); \+   } while(false)+   */+#endif++#ifndef VMA_DEBUG_LOG+    #define VMA_DEBUG_LOG(str)   VMA_DEBUG_LOG_FORMAT("%s", (str))+#endif++#ifndef VMA_LEAK_LOG_FORMAT+    #define VMA_LEAK_LOG_FORMAT(format, ...)   VMA_DEBUG_LOG_FORMAT(format, __VA_ARGS__)+#endif++#ifndef VMA_CLASS_NO_COPY+    #define VMA_CLASS_NO_COPY(className) \+        private: \+            className(const className&) = delete; \+            className& operator=(const className&) = delete;+#endif+#ifndef VMA_CLASS_NO_COPY_NO_MOVE+    #define VMA_CLASS_NO_COPY_NO_MOVE(className) \+        private: \+            className(const className&) = delete; \+            className(className&&) = delete; \+            className& operator=(const className&) = delete; \+            className& operator=(className&&) = delete;+#endif++// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.+#if VMA_STATS_STRING_ENABLED+    static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num)+    {+        snprintf(outStr, strLen, "%" PRIu32, num);+    }+    static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num)+    {+        snprintf(outStr, strLen, "%" PRIu64, num);+    }+    static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr)+    {+        snprintf(outStr, strLen, "%p", ptr);+    }+#endif++#ifndef VMA_MUTEX+    class VmaMutex+    {+    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutex)+    public:+        VmaMutex() = default;+        void Lock() { m_Mutex.lock(); }+        void Unlock() { m_Mutex.unlock(); }+        bool TryLock() { return m_Mutex.try_lock(); }+    private:+        std::mutex m_Mutex;+    };+    #define VMA_MUTEX VmaMutex+#endif++// Read-write mutex, where "read" is shared access, "write" is exclusive access.+#ifndef VMA_RW_MUTEX+    #if VMA_USE_STL_SHARED_MUTEX+        // Use std::shared_mutex from C++17.+        #include <shared_mutex>+        class VmaRWMutex+        {+        public:+            void LockRead() { m_Mutex.lock_shared(); }+            void UnlockRead() { m_Mutex.unlock_shared(); }+            bool TryLockRead() { return m_Mutex.try_lock_shared(); }+            void LockWrite() { m_Mutex.lock(); }+            void UnlockWrite() { m_Mutex.unlock(); }+            bool TryLockWrite() { return m_Mutex.try_lock(); }+        private:+            std::shared_mutex m_Mutex;+        };+        #define VMA_RW_MUTEX VmaRWMutex+    #elif defined(_WIN32) && defined(WINVER) && defined(SRWLOCK_INIT) && WINVER >= 0x0600+        // Use SRWLOCK from WinAPI.+        // Minimum supported client = Windows Vista, server = Windows Server 2008.+        class VmaRWMutex+        {+        public:+            VmaRWMutex() { InitializeSRWLock(&m_Lock); }+            void LockRead() { AcquireSRWLockShared(&m_Lock); }+            void UnlockRead() { ReleaseSRWLockShared(&m_Lock); }+            bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; }+            void LockWrite() { AcquireSRWLockExclusive(&m_Lock); }+            void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); }+            bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; }+        private:+            SRWLOCK m_Lock;+        };+        #define VMA_RW_MUTEX VmaRWMutex+    #else+        // Less efficient fallback: Use normal mutex.+        class VmaRWMutex+        {+        public:+            void LockRead() { m_Mutex.Lock(); }+            void UnlockRead() { m_Mutex.Unlock(); }+            bool TryLockRead() { return m_Mutex.TryLock(); }+            void LockWrite() { m_Mutex.Lock(); }+            void UnlockWrite() { m_Mutex.Unlock(); }+            bool TryLockWrite() { return m_Mutex.TryLock(); }+        private:+            VMA_MUTEX m_Mutex;+        };+        #define VMA_RW_MUTEX VmaRWMutex+    #endif // #if VMA_USE_STL_SHARED_MUTEX+#endif // #ifndef VMA_RW_MUTEX++/*+If providing your own implementation, you need to implement a subset of std::atomic.+*/+#ifndef VMA_ATOMIC_UINT32+    #include <atomic>+    #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>+#endif++#ifndef VMA_ATOMIC_UINT64+    #include <atomic>+    #define VMA_ATOMIC_UINT64 std::atomic<uint64_t>+#endif++#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY+    /**+    Every allocation will have its own memory block.+    Define to 1 for debugging purposes only.+    */+    #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0)+#endif++#ifndef VMA_MIN_ALIGNMENT+    /**+    Minimum alignment of all allocations, in bytes.+    Set to more than 1 for debugging purposes. Must be power of two.+    */+    #ifdef VMA_DEBUG_ALIGNMENT // Old name+        #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT+    #else+        #define VMA_MIN_ALIGNMENT (1)+    #endif+#endif++#ifndef VMA_DEBUG_MARGIN+    /**+    Minimum margin after every allocation, in bytes.+    Set nonzero for debugging purposes only.+    */+    #define VMA_DEBUG_MARGIN (0)+#endif++#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS+    /**+    Define this macro to 1 to automatically fill new allocations and destroyed+    allocations with some bit pattern.+    */+    #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0)+#endif++#ifndef VMA_DEBUG_DETECT_CORRUPTION+    /**+    Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to+    enable writing magic value to the margin after every allocation and+    validating it, so that memory corruptions (out-of-bounds writes) are detected.+    */+    #define VMA_DEBUG_DETECT_CORRUPTION (0)+#endif++#ifndef VMA_DEBUG_GLOBAL_MUTEX+    /**+    Set this to 1 for debugging purposes only, to enable single mutex protecting all+    entry calls to the library. Can be useful for debugging multithreading issues.+    */+    #define VMA_DEBUG_GLOBAL_MUTEX (0)+#endif++#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY+    /**+    Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.+    Set to more than 1 for debugging purposes only. Must be power of two.+    */+    #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)+#endif++#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT+    /*+    Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount+    and return error instead of leaving up to Vulkan implementation what to do in such cases.+    */+    #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (1)+#endif++#ifndef VMA_DEBUG_DONT_EXCEED_HEAP_SIZE_WITH_ALLOCATION_SIZE+    /*+    Set this to 1 to make VMA never exceed VkPhysicalDeviceMemoryProperties::memoryHeaps[i].size+    with a single allocation size VkMemoryAllocateInfo::allocationSize+    and return error instead of leaving up to Vulkan implementation what to do in such cases.+    It protects agaist validation error VUID-vkAllocateMemory-pAllocateInfo-01713.+    On the other hand, allowing exceeding this size may result in a successful allocation despite the validation error.+    */+    #define VMA_DEBUG_DONT_EXCEED_HEAP_SIZE_WITH_ALLOCATION_SIZE (1)+#endif++#ifndef VMA_SMALL_HEAP_MAX_SIZE+   /// Maximum size of a memory heap in Vulkan to consider it "small".+   #define VMA_SMALL_HEAP_MAX_SIZE (1024ULL * 1024 * 1024)+#endif++#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE+   /// Default size of a block allocated as single VkDeviceMemory from a "large" heap.+   #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ULL * 1024 * 1024)+#endif++/*+Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called+or a persistently mapped allocation is created and destroyed several times in a row.+It keeps additional +1 mapping of a device memory block to prevent calling actual+vkMapMemory/vkUnmapMemory too many times, which may improve performance and help+tools like RenderDoc.+*/+#ifndef VMA_MAPPING_HYSTERESIS_ENABLED+    #define VMA_MAPPING_HYSTERESIS_ENABLED 1+#endif++#define VMA_VALIDATE(cond) do { if(!(cond)) { \+        VMA_ASSERT(0 && "Validation failed: " #cond); \+        return false; \+    } } while(false)++/*******************************************************************************+END OF CONFIGURATION+*/+#endif // _VMA_CONFIGURATION+++static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC;+static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF;+// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F.+static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666;++// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants.+static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040;+static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080;+static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000;+static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200;+static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000;+static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000U;+static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32;+static const uint32_t VMA_VENDOR_ID_AMD = 4098;++// This one is tricky. Vulkan specification defines this code as available since+// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131.+// See pull request #207.+#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13)+++#if VMA_STATS_STRING_ENABLED+// Correspond to values of enum VmaSuballocationType.+static const char* const VMA_SUBALLOCATION_TYPE_NAMES[] =+{+    "FREE",+    "UNKNOWN",+    "BUFFER",+    "IMAGE_UNKNOWN",+    "IMAGE_LINEAR",+    "IMAGE_OPTIMAL",+};+#endif++static const VkAllocationCallbacks VmaEmptyAllocationCallbacks =+    { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };+++#ifndef _VMA_ENUM_DECLARATIONS++enum VmaSuballocationType+{+    VMA_SUBALLOCATION_TYPE_FREE = 0,+    VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,+    VMA_SUBALLOCATION_TYPE_BUFFER = 2,+    VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,+    VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,+    VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,+    VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF+};++enum VMA_CACHE_OPERATION+{+    VMA_CACHE_FLUSH,+    VMA_CACHE_INVALIDATE+};++enum class VmaAllocationRequestType+{+    Normal,+    TLSF,+    // Used by "Linear" algorithm.+    UpperAddress,+    EndOf1st,+    EndOf2nd,+};++#endif // _VMA_ENUM_DECLARATIONS++#ifndef _VMA_FORWARD_DECLARATIONS+// Opaque handle used by allocation algorithms to identify single allocation in any conforming way.+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle);++struct VmaMutexLock;+struct VmaMutexLockRead;+struct VmaMutexLockWrite;++template<typename T>+struct AtomicTransactionalIncrement;++template<typename T>+struct VmaStlAllocator;++template<typename T, typename AllocatorT>+class VmaVector;++template<typename T, typename AllocatorT, size_t N>+class VmaSmallVector;++template<typename T>+class VmaPoolAllocator;++template<typename T>+struct VmaListItem;++template<typename T>+class VmaRawList;++template<typename T, typename AllocatorT>+class VmaList;++template<typename ItemTypeTraits>+class VmaIntrusiveLinkedList;++#if VMA_STATS_STRING_ENABLED+class VmaStringBuilder;+class VmaJsonWriter;+#endif++class VmaDeviceMemoryBlock;++struct VmaDedicatedAllocationListItemTraits;+class VmaDedicatedAllocationList;++struct VmaSuballocation;+struct VmaSuballocationOffsetLess;+struct VmaSuballocationOffsetGreater;+struct VmaSuballocationItemSizeLess;++typedef VmaList<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> VmaSuballocationList;++struct VmaAllocationRequest;++class VmaBlockMetadata;+class VmaBlockMetadata_Linear;+class VmaBlockMetadata_TLSF;++class VmaBlockVector;++struct VmaPoolListItemTraits;++struct VmaCurrentBudgetData;++class VmaAllocationObjectAllocator;++#endif // _VMA_FORWARD_DECLARATIONS+++#ifndef _VMA_FUNCTIONS++/*+Returns number of bits set to 1 in (v).++On specific platforms and compilers you can use intrinsics like:++Visual Studio:+    return __popcnt(v);+GCC, Clang:+    return static_cast<uint32_t>(__builtin_popcount(v));++Define macro VMA_COUNT_BITS_SET to provide your optimized implementation.+But you need to check in runtime whether user's CPU supports these, as some old processors don't.+*/+static inline uint32_t VmaCountBitsSet(uint32_t v)+{+#if VMA_CPP20+    return std::popcount(v);+#else+    uint32_t c = v - ((v >> 1) & 0x55555555);+    c = ((c >> 2) & 0x33333333) + (c & 0x33333333);+    c = ((c >> 4) + c) & 0x0F0F0F0F;+    c = ((c >> 8) + c) & 0x00FF00FF;+    c = ((c >> 16) + c) & 0x0000FFFF;+    return c;+#endif+}++static inline uint8_t VmaBitScanLSB(uint64_t mask)+{+#if defined(_MSC_VER) && defined(_WIN64)+    unsigned long pos;+    if (_BitScanForward64(&pos, mask))+        return static_cast<uint8_t>(pos);+    return UINT8_MAX;+#elif VMA_CPP20+    if(mask != 0)+        return static_cast<uint8_t>(std::countr_zero(mask));+    return UINT8_MAX;+#elif defined __GNUC__ || defined __clang__+    return static_cast<uint8_t>(__builtin_ffsll(mask)) - 1U;+#else+    uint8_t pos = 0;+    uint64_t bit = 1;+    do+    {+        if (mask & bit)+            return pos;+        bit <<= 1;+    } while (pos++ < 63);+    return UINT8_MAX;+#endif+}++static inline uint8_t VmaBitScanLSB(uint32_t mask)+{+#ifdef _MSC_VER+    unsigned long pos;+    if (_BitScanForward(&pos, mask))+        return static_cast<uint8_t>(pos);+    return UINT8_MAX;+#elif VMA_CPP20+    if(mask != 0)+        return static_cast<uint8_t>(std::countr_zero(mask));+    return UINT8_MAX;+#elif defined __GNUC__ || defined __clang__+    return static_cast<uint8_t>(__builtin_ffs(mask)) - 1U;+#else+    uint8_t pos = 0;+    uint32_t bit = 1;+    do+    {+        if (mask & bit)+            return pos;+        bit <<= 1;+    } while (pos++ < 31);+    return UINT8_MAX;+#endif+}++static inline uint8_t VmaBitScanMSB(uint64_t mask)+{+#if defined(_MSC_VER) && defined(_WIN64)+    unsigned long pos;+    if (_BitScanReverse64(&pos, mask))+        return static_cast<uint8_t>(pos);+#elif VMA_CPP20+    if(mask != 0)+        return 63 - static_cast<uint8_t>(std::countl_zero(mask));+#elif defined __GNUC__ || defined __clang__+    if (mask != 0)+        return 63 - static_cast<uint8_t>(__builtin_clzll(mask));+#else+    uint8_t pos = 63;+    uint64_t bit = 1ULL << 63;+    do+    {+        if (mask & bit)+            return pos;+        bit >>= 1;+    } while (pos-- > 0);+#endif+    return UINT8_MAX;+}++static inline uint8_t VmaBitScanMSB(uint32_t mask)+{+#ifdef _MSC_VER+    unsigned long pos;+    if (_BitScanReverse(&pos, mask))+        return static_cast<uint8_t>(pos);+#elif VMA_CPP20+    if(mask != 0)+        return 31 - static_cast<uint8_t>(std::countl_zero(mask));+#elif defined __GNUC__ || defined __clang__+    if (mask != 0)+        return 31 - static_cast<uint8_t>(__builtin_clz(mask));+#else+    uint8_t pos = 31;+    uint32_t bit = 1UL << 31;+    do+    {+        if (mask & bit)+            return pos;+        bit >>= 1;+    } while (pos-- > 0);+#endif+    return UINT8_MAX;+}++/*+Returns true if given number is a power of two.+T must be unsigned integer number or signed integer but always nonnegative.+For 0 returns true.+*/+template <typename T>+inline bool VmaIsPow2(T x)+{+    return (x & (x - 1)) == 0;+}++// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.+// Use types like uint32_t, uint64_t as T.+template <typename T>+static inline T VmaAlignUp(T val, T alignment)+{+    VMA_HEAVY_ASSERT(VmaIsPow2(alignment));+    return (val + alignment - 1) & ~(alignment - 1);+}++// Aligns given value down to nearest multiply of align value. For example: VmaAlignDown(11, 8) = 8.+// Use types like uint32_t, uint64_t as T.+template <typename T>+static inline T VmaAlignDown(T val, T alignment)+{+    VMA_HEAVY_ASSERT(VmaIsPow2(alignment));+    return val & ~(alignment - 1);+}++// Division with mathematical rounding to nearest number.+template <typename T>+static inline T VmaRoundDiv(T x, T y)+{+    return (x + (y / (T)2)) / y;+}++// Divide by 'y' and round up to nearest integer.+template <typename T>+static inline T VmaDivideRoundingUp(T x, T y)+{+    return (x + y - (T)1) / y;+}++// Returns smallest power of 2 greater or equal to v.+static inline uint32_t VmaNextPow2(uint32_t v)+{+    v--;+    v |= v >> 1;+    v |= v >> 2;+    v |= v >> 4;+    v |= v >> 8;+    v |= v >> 16;+    v++;+    return v;+}++static inline uint64_t VmaNextPow2(uint64_t v)+{+    v--;+    v |= v >> 1;+    v |= v >> 2;+    v |= v >> 4;+    v |= v >> 8;+    v |= v >> 16;+    v |= v >> 32;+    v++;+    return v;+}++// Returns largest power of 2 less or equal to v.+static inline uint32_t VmaPrevPow2(uint32_t v)+{+    v |= v >> 1;+    v |= v >> 2;+    v |= v >> 4;+    v |= v >> 8;+    v |= v >> 16;+    v = v ^ (v >> 1);+    return v;+}++static inline uint64_t VmaPrevPow2(uint64_t v)+{+    v |= v >> 1;+    v |= v >> 2;+    v |= v >> 4;+    v |= v >> 8;+    v |= v >> 16;+    v |= v >> 32;+    v = v ^ (v >> 1);+    return v;+}++static inline bool VmaStrIsEmpty(const char* pStr)+{+    return pStr == VMA_NULL || *pStr == '\0';+}++/*+Returns true if two memory blocks occupy overlapping pages.+ResourceA must be in less memory offset than ResourceB.++Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"+chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".+*/+static inline bool VmaBlocksOnSamePage(+    VkDeviceSize resourceAOffset,+    VkDeviceSize resourceASize,+    VkDeviceSize resourceBOffset,+    VkDeviceSize pageSize)+{+    VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);+    VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;+    VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);+    VkDeviceSize resourceBStart = resourceBOffset;+    VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);+    return resourceAEndPage == resourceBStartPage;+}++/*+Returns true if given suballocation types could conflict and must respect+VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer+or linear image and another one is optimal image. If type is unknown, behave+conservatively.+*/+static inline bool VmaIsBufferImageGranularityConflict(+    VmaSuballocationType suballocType1,+    VmaSuballocationType suballocType2)+{+    if (suballocType1 > suballocType2)+    {+        std::swap(suballocType1, suballocType2);+    }++    switch (suballocType1)+    {+    case VMA_SUBALLOCATION_TYPE_FREE:+        return false;+    case VMA_SUBALLOCATION_TYPE_UNKNOWN:+        return true;+    case VMA_SUBALLOCATION_TYPE_BUFFER:+        return+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;+    case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:+        return+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;+    case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:+        return+            suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;+    case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:+        return false;+    default:+        VMA_ASSERT(0);+        return true;+    }+}++static void VmaWriteMagicValue(void* pData, VkDeviceSize offset)+{+#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION+    uint32_t* pDst = (uint32_t*)((char*)pData + offset);+    const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);+    for (size_t i = 0; i < numberCount; ++i, ++pDst)+    {+        *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE;+    }+#else+    // no-op+#endif+}++static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset)+{+#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION+    const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset);+    const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t);+    for (size_t i = 0; i < numberCount; ++i, ++pSrc)+    {+        if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE)+        {+            return false;+        }+    }+#endif+    return true;+}++/*+Fills structure with parameters of an example buffer to be used for transfers+during GPU memory defragmentation.+*/+static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo)+{+    memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo));+    outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;+    outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;+    outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size.+}+++/*+Performs binary search and returns iterator to first element that is greater or+equal to (key), according to comparison (cmp).++Cmp should return true if first argument is less than second argument.++Returned value is the found element, if present in the collection or place where+new element with value (key) should be inserted.+*/+template <typename CmpLess, typename IterT, typename KeyT>+static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)+{+    size_t down = 0;+    size_t up = size_t(end - beg);+    while (down < up)+    {+        const size_t mid = down + (up - down) / 2;  // Overflow-safe midpoint calculation+        if (cmp(*(beg + mid), key))+        {+            down = mid + 1;+        }+        else+        {+            up = mid;+        }+    }+    return beg + down;+}++template<typename CmpLess, typename IterT, typename KeyT>+IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp)+{+    IterT it = VmaBinaryFindFirstNotLess<CmpLess, IterT, KeyT>(+        beg, end, value, cmp);+    if (it == end ||+        (!cmp(*it, value) && !cmp(value, *it)))+    {+        return it;+    }+    return end;+}++/*+Returns true if all pointers in the array are not-null and unique.+Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.+T must be pointer type, e.g. VmaAllocation, VmaPool.+*/+template<typename T>+static bool VmaValidatePointerArray(uint32_t count, const T* arr)+{+    for (uint32_t i = 0; i < count; ++i)+    {+        const T iPtr = arr[i];+        if (iPtr == VMA_NULL)+        {+            return false;+        }+        for (uint32_t j = i + 1; j < count; ++j)+        {+            if (iPtr == arr[j])+            {+                return false;+            }+        }+    }+    return true;+}++template<typename MainT, typename NewT>+static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct)+{+    newStruct->pNext = mainStruct->pNext;+    mainStruct->pNext = newStruct;+}+// Finds structure with s->sType == sType in mainStruct->pNext chain.+// Returns pointer to it. If not found, returns null.+template<typename FindT, typename MainT>+static inline const FindT* VmaPnextChainFind(const MainT* mainStruct, VkStructureType sType)+{+    for(const VkBaseInStructure* s = (const VkBaseInStructure*)mainStruct->pNext;+        s != VMA_NULL; s = s->pNext)+    {+        if(s->sType == sType)+        {+            return (const FindT*)s;+        }+    }+    return VMA_NULL;+}++// An abstraction over buffer or image `usage` flags, depending on available extensions.+struct VmaBufferImageUsage+{+#if VMA_KHR_MAINTENANCE5+    typedef uint64_t BaseType; // VkFlags64+#else+    typedef uint32_t BaseType; // VkFlags32+#endif++    static const VmaBufferImageUsage UNKNOWN;++    BaseType Value;++    VmaBufferImageUsage() { *this = UNKNOWN; }+    explicit VmaBufferImageUsage(BaseType usage) : Value(usage) { }+    VmaBufferImageUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5);+    explicit VmaBufferImageUsage(const VkImageCreateInfo &createInfo);++    bool operator==(const VmaBufferImageUsage& rhs) const { return Value == rhs.Value; }+    bool operator!=(const VmaBufferImageUsage& rhs) const { return Value != rhs.Value; }++    bool Contains(BaseType flag) const { return (Value & flag) != 0; }+    bool ContainsDeviceAccess() const+    {+        // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same as VK_BUFFER_IMAGE_TRANSFER*.+        return (Value & ~BaseType(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0;+    }+};++const VmaBufferImageUsage VmaBufferImageUsage::UNKNOWN = VmaBufferImageUsage(0);++VmaBufferImageUsage::VmaBufferImageUsage(const VkBufferCreateInfo &createInfo,+    bool useKhrMaintenance5)+{+#if VMA_KHR_MAINTENANCE5+    if(useKhrMaintenance5)+    {+        // If VkBufferCreateInfo::pNext chain contains VkBufferUsageFlags2CreateInfoKHR,+        // take usage from it and ignore VkBufferCreateInfo::usage, per specification+        // of the VK_KHR_maintenance5 extension.+        const VkBufferUsageFlags2CreateInfoKHR* const usageFlags2 =+            VmaPnextChainFind<VkBufferUsageFlags2CreateInfoKHR>(&createInfo, VK_STRUCTURE_TYPE_BUFFER_USAGE_FLAGS_2_CREATE_INFO_KHR);+        if(usageFlags2 != VMA_NULL)+        {+            this->Value = usageFlags2->usage;+            return;+        }+    }+#endif++    this->Value = (BaseType)createInfo.usage;+}++VmaBufferImageUsage::VmaBufferImageUsage(const VkImageCreateInfo &createInfo)+    : Value((BaseType)createInfo.usage)+{+    // Maybe in the future there will be VK_KHR_maintenanceN extension with structure+    // VkImageUsageFlags2CreateInfoKHR, like the one for buffers...+}++// This is the main algorithm that guides the selection of a memory type best for an allocation -+// converts usage to required/preferred/not preferred flags.+static bool FindMemoryPreferences(+    bool isIntegratedGPU,+    const VmaAllocationCreateInfo& allocCreateInfo,+    VmaBufferImageUsage bufImgUsage,+    VkMemoryPropertyFlags& outRequiredFlags,+    VkMemoryPropertyFlags& outPreferredFlags,+    VkMemoryPropertyFlags& outNotPreferredFlags)+{+    outRequiredFlags = allocCreateInfo.requiredFlags;+    outPreferredFlags = allocCreateInfo.preferredFlags;+    outNotPreferredFlags = 0;++    switch(allocCreateInfo.usage)+    {+    case VMA_MEMORY_USAGE_UNKNOWN:+        break;+    case VMA_MEMORY_USAGE_GPU_ONLY:+        if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)+        {+            outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+        }+        break;+    case VMA_MEMORY_USAGE_CPU_ONLY:+        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;+        break;+    case VMA_MEMORY_USAGE_CPU_TO_GPU:+        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+        if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)+        {+            outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+        }+        break;+    case VMA_MEMORY_USAGE_GPU_TO_CPU:+        outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+        outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;+        break;+    case VMA_MEMORY_USAGE_CPU_COPY:+        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+        break;+    case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED:+        outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;+        break;+    case VMA_MEMORY_USAGE_AUTO:+    case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE:+    case VMA_MEMORY_USAGE_AUTO_PREFER_HOST:+    {+        if(bufImgUsage == VmaBufferImageUsage::UNKNOWN)+        {+            VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known."+                " Maybe you use VkBufferUsageFlags2CreateInfoKHR but forgot to use VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT?" );+            return false;+        }++        const bool deviceAccess = bufImgUsage.ContainsDeviceAccess();+        const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0;+        const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0;+        const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0;+        const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;+        const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST;++        // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU.+        if(hostAccessRandom)+        {+            // Prefer cached. Cannot require it, because some platforms don't have it (e.g. Raspberry Pi - see #362)!+            outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;++            if (!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)+            {+                // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL.+                // Omitting HOST_VISIBLE here is intentional.+                // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one.+                // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list.+                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+            }+            else+            {+                // Always CPU memory.+                outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+            }+        }+        // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined.+        else if(hostAccessSequentialWrite)+        {+            // Want uncached and write-combined.+            outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;++            if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost)+            {+                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+            }+            else+            {+                outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+                // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame)+                if(deviceAccess)+                {+                    // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory.+                    if(preferHost)+                        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+                    else+                        outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+                }+                // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU)+                else+                {+                    // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory.+                    if(preferDevice)+                        outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+                    else+                        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+                }+            }+        }+        // No CPU access+        else+        {+            // if(deviceAccess)+            //+            // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory,+            // unless there is a clear preference from the user not to do so.+            //+            // else:+            //+            // No direct GPU access, no CPU access, just transfers.+            // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or+            // a "swap file" copy to free some GPU memory (then better CPU memory).+            // Up to the user to decide. If no preferece, assume the former and choose GPU memory.++            if(preferHost)+                outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+            else+                outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;+        }+        break;+    }+    default:+        VMA_ASSERT(0);+    }++    // Avoid DEVICE_COHERENT unless explicitly requested.+    if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) &+        (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0)+    {+        outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY;+    }++    return true;+}++////////////////////////////////////////////////////////////////////////////////+// Memory allocation++static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)+{+    void* result = VMA_NULL;+    if ((pAllocationCallbacks != VMA_NULL) &&+        (pAllocationCallbacks->pfnAllocation != VMA_NULL))+    {+        result = (*pAllocationCallbacks->pfnAllocation)(+            pAllocationCallbacks->pUserData,+            size,+            alignment,+            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);+    }+    else+    {+        result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);+    }+    VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed.");+    return result;+}++static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)+{+    if ((pAllocationCallbacks != VMA_NULL) &&+        (pAllocationCallbacks->pfnFree != VMA_NULL))+    {+        (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);+    }+    else+    {+        VMA_SYSTEM_ALIGNED_FREE(ptr);+    }+}++template<typename T>+static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)+{+    return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));+}++template<typename T>+static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)+{+    return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));+}++#define vma_new(allocator, type)   new(VmaAllocate<type>(allocator))(type)++#define vma_new_array(allocator, type, count)   new(VmaAllocateArray<type>((allocator), (count)))(type)++template<typename T>+static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)+{+    ptr->~T();+    VmaFree(pAllocationCallbacks, ptr);+}++template<typename T>+static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)+{+    if (ptr != VMA_NULL)+    {+        for (size_t i = count; i--; )+        {+            ptr[i].~T();+        }+        VmaFree(pAllocationCallbacks, ptr);+    }+}++static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr)+{+    if (srcStr != VMA_NULL)+    {+        const size_t len = strlen(srcStr);+        char* const result = vma_new_array(allocs, char, len + 1);+        memcpy(result, srcStr, len + 1);+        return result;+    }+    return VMA_NULL;+}++#if VMA_STATS_STRING_ENABLED+static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen)+{+    if (srcStr != VMA_NULL)+    {+        char* const result = vma_new_array(allocs, char, strLen + 1);+        memcpy(result, srcStr, strLen);+        result[strLen] = '\0';+        return result;+    }+    return VMA_NULL;+}+#endif // VMA_STATS_STRING_ENABLED++static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str)+{+    if (str != VMA_NULL)+    {+        const size_t len = strlen(str);+        vma_delete_array(allocs, str, len + 1);+    }+}++template<typename CmpLess, typename VectorT>+size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value)+{+    const size_t indexToInsert = VmaBinaryFindFirstNotLess(+        vector.data(),+        vector.data() + vector.size(),+        value,+        CmpLess()) - vector.data();+    VmaVectorInsert(vector, indexToInsert, value);+    return indexToInsert;+}++template<typename CmpLess, typename VectorT>+bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value)+{+    CmpLess comparator;+    typename VectorT::iterator it = VmaBinaryFindFirstNotLess(+        vector.begin(),+        vector.end(),+        value,+        comparator);+    if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it))+    {+        size_t indexToRemove = it - vector.begin();+        VmaVectorRemove(vector, indexToRemove);+        return true;+    }+    return false;+}+#endif // _VMA_FUNCTIONS++#ifndef _VMA_STATISTICS_FUNCTIONS++static void VmaClearStatistics(VmaStatistics& outStats)+{+    outStats.blockCount = 0;+    outStats.allocationCount = 0;+    outStats.blockBytes = 0;+    outStats.allocationBytes = 0;+}++static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src)+{+    inoutStats.blockCount += src.blockCount;+    inoutStats.allocationCount += src.allocationCount;+    inoutStats.blockBytes += src.blockBytes;+    inoutStats.allocationBytes += src.allocationBytes;+}++static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats)+{+    VmaClearStatistics(outStats.statistics);+    outStats.unusedRangeCount = 0;+    outStats.allocationSizeMin = VK_WHOLE_SIZE;+    outStats.allocationSizeMax = 0;+    outStats.unusedRangeSizeMin = VK_WHOLE_SIZE;+    outStats.unusedRangeSizeMax = 0;+}++static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size)+{+    inoutStats.statistics.allocationCount++;+    inoutStats.statistics.allocationBytes += size;+    inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size);+    inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size);+}++static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size)+{+    inoutStats.unusedRangeCount++;+    inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size);+    inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size);+}++static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src)+{+    VmaAddStatistics(inoutStats.statistics, src.statistics);+    inoutStats.unusedRangeCount += src.unusedRangeCount;+    inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin);+    inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax);+    inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin);+    inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax);+}++#endif // _VMA_STATISTICS_FUNCTIONS++#ifndef _VMA_MUTEX_LOCK+// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).+struct VmaMutexLock+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLock)+public:+    explicit VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) :+        m_pMutex(useMutex ? &mutex : VMA_NULL)+    {+        if (m_pMutex) { m_pMutex->Lock(); }+    }+    ~VmaMutexLock() {  if (m_pMutex) { m_pMutex->Unlock(); } }++private:+    VMA_MUTEX* m_pMutex;+};++// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading.+struct VmaMutexLockRead+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockRead)+public:+    VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) :+        m_pMutex(useMutex ? &mutex : VMA_NULL)+    {+        if (m_pMutex) { m_pMutex->LockRead(); }+    }+    ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } }++private:+    VMA_RW_MUTEX* m_pMutex;+};++// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing.+struct VmaMutexLockWrite+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaMutexLockWrite)+public:+    VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex)+        : m_pMutex(useMutex ? &mutex : VMA_NULL)+    {+        if (m_pMutex) { m_pMutex->LockWrite(); }+    }+    ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } }++private:+    VMA_RW_MUTEX* m_pMutex;+};++#if VMA_DEBUG_GLOBAL_MUTEX+    static VMA_MUTEX gDebugGlobalMutex;+    #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true);+#else+    #define VMA_DEBUG_GLOBAL_MUTEX_LOCK+#endif+#endif // _VMA_MUTEX_LOCK++#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT+// An object that increments given atomic but decrements it back in the destructor unless Commit() is called.+template<typename AtomicT>+struct AtomicTransactionalIncrement+{+public:+    using T = decltype(AtomicT().load());++    ~AtomicTransactionalIncrement()+    {+        if(m_Atomic)+            --(*m_Atomic);+    }++    void Commit() { m_Atomic = VMA_NULL; }+    T Increment(AtomicT* atomic)+    {+        m_Atomic = atomic;+        return m_Atomic->fetch_add(1);+    }++private:+    AtomicT* m_Atomic = VMA_NULL;+};+#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT++#ifndef _VMA_STL_ALLOCATOR+// STL-compatible allocator.+template<typename T>+struct VmaStlAllocator+{+    const VkAllocationCallbacks* const m_pCallbacks;+    typedef T value_type;++    explicit VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {}+    template<typename U>+    explicit VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) {}+    VmaStlAllocator(const VmaStlAllocator&) = default;+    VmaStlAllocator& operator=(const VmaStlAllocator&) = delete;++    T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }+    void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }++    template<typename U>+    bool operator==(const VmaStlAllocator<U>& rhs) const+    {+        return m_pCallbacks == rhs.m_pCallbacks;+    }+    template<typename U>+    bool operator!=(const VmaStlAllocator<U>& rhs) const+    {+        return m_pCallbacks != rhs.m_pCallbacks;+    }+};+#endif // _VMA_STL_ALLOCATOR++#ifndef _VMA_VECTOR+/* Class with interface compatible with subset of std::vector.+T must be POD because constructors and destructors are not called and memcpy is+used for these objects. */+template<typename T, typename AllocatorT>+class VmaVector+{+public:+    typedef T value_type;+    typedef T* iterator;+    typedef const T* const_iterator;++    explicit VmaVector(const AllocatorT& allocator);+    VmaVector(size_t count, const AllocatorT& allocator);+    // This version of the constructor is here for compatibility with pre-C++14 std::vector.+    // value is unused.+    VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {}+    VmaVector(const VmaVector<T, AllocatorT>& src);+    VmaVector& operator=(const VmaVector& rhs);+    ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); }++    bool empty() const { return m_Count == 0; }+    size_t size() const { return m_Count; }+    T* data() { return m_pArray; }+    T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }+    T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }+    const T* data() const { return m_pArray; }+    const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; }+    const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; }++    iterator begin() { return m_pArray; }+    iterator end() { return m_pArray + m_Count; }+    const_iterator cbegin() const { return m_pArray; }+    const_iterator cend() const { return m_pArray + m_Count; }+    const_iterator begin() const { return cbegin(); }+    const_iterator end() const { return cend(); }++    void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }+    void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }+    void push_front(const T& src) { insert(0, src); }++    void push_back(const T& src);+    void reserve(size_t newCapacity, bool freeMemory = false);+    void resize(size_t newCount);+    void clear() { resize(0); }+    void shrink_to_fit();+    void insert(size_t index, const T& src);+    void remove(size_t index);++    T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }+    const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; }++private:+    AllocatorT m_Allocator;+    T* m_pArray;+    size_t m_Count;+    size_t m_Capacity;+};++#ifndef _VMA_VECTOR_FUNCTIONS+template<typename T, typename AllocatorT>+VmaVector<T, AllocatorT>::VmaVector(const AllocatorT& allocator)+    : m_Allocator(allocator),+    m_pArray(VMA_NULL),+    m_Count(0),+    m_Capacity(0) {}++template<typename T, typename AllocatorT>+VmaVector<T, AllocatorT>::VmaVector(size_t count, const AllocatorT& allocator)+    : m_Allocator(allocator),+    m_pArray(count ? (T*)VmaAllocateArray<T>(allocator.m_pCallbacks, count) : VMA_NULL),+    m_Count(count),+    m_Capacity(count) {}++template<typename T, typename AllocatorT>+VmaVector<T, AllocatorT>::VmaVector(const VmaVector& src)+    : m_Allocator(src.m_Allocator),+    m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL),+    m_Count(src.m_Count),+    m_Capacity(src.m_Count)+{+    if (m_Count != 0)+    {+        memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));+    }+}++template<typename T, typename AllocatorT>+VmaVector<T, AllocatorT>& VmaVector<T, AllocatorT>::operator=(const VmaVector& rhs)+{+    if (&rhs != this)+    {+        resize(rhs.m_Count);+        if (m_Count != 0)+        {+            memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));+        }+    }+    return *this;+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::push_back(const T& src)+{+    const size_t newIndex = size();+    resize(newIndex + 1);+    m_pArray[newIndex] = src;+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::reserve(size_t newCapacity, bool freeMemory)+{+    newCapacity = VMA_MAX(newCapacity, m_Count);++    if ((newCapacity < m_Capacity) && !freeMemory)+    {+        newCapacity = m_Capacity;+    }++    if (newCapacity != m_Capacity)+    {+        T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;+        if (m_Count != 0)+        {+            memcpy(newArray, m_pArray, m_Count * sizeof(T));+        }+        VmaFree(m_Allocator.m_pCallbacks, m_pArray);+        m_Capacity = newCapacity;+        m_pArray = newArray;+    }+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::resize(size_t newCount)+{+    size_t newCapacity = m_Capacity;+    if (newCount > m_Capacity)+    {+        newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));+    }++    if (newCapacity != m_Capacity)+    {+        T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;+        const size_t elementsToCopy = VMA_MIN(m_Count, newCount);+        if (elementsToCopy != 0)+        {+            memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));+        }+        VmaFree(m_Allocator.m_pCallbacks, m_pArray);+        m_Capacity = newCapacity;+        m_pArray = newArray;+    }++    m_Count = newCount;+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::shrink_to_fit()+{+    if (m_Capacity > m_Count)+    {+        T* newArray = VMA_NULL;+        if (m_Count > 0)+        {+            newArray = VmaAllocateArray<T>(m_Allocator.m_pCallbacks, m_Count);+            memcpy(newArray, m_pArray, m_Count * sizeof(T));+        }+        VmaFree(m_Allocator.m_pCallbacks, m_pArray);+        m_Capacity = m_Count;+        m_pArray = newArray;+    }+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::insert(size_t index, const T& src)+{+    VMA_HEAVY_ASSERT(index <= m_Count);+    const size_t oldCount = size();+    resize(oldCount + 1);+    if (index < oldCount)+    {+        memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));+    }+    m_pArray[index] = src;+}++template<typename T, typename AllocatorT>+void VmaVector<T, AllocatorT>::remove(size_t index)+{+    VMA_HEAVY_ASSERT(index < m_Count);+    const size_t oldCount = size();+    if (index < oldCount - 1)+    {+        memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));+    }+    resize(oldCount - 1);+}+#endif // _VMA_VECTOR_FUNCTIONS++template<typename T, typename allocatorT>+static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)+{+    vec.insert(index, item);+}++template<typename T, typename allocatorT>+static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)+{+    vec.remove(index);+}+#endif // _VMA_VECTOR++#ifndef _VMA_SMALL_VECTOR+/*+This is a vector (a variable-sized array), optimized for the case when the array is small.++It contains some number of elements in-place, which allows it to avoid heap allocation+when the actual number of elements is below that threshold. This allows normal "small"+cases to be fast without losing generality for large inputs.+*/+template<typename T, typename AllocatorT, size_t N>+class VmaSmallVector+{+public:+    typedef T value_type;+    typedef T* iterator;++    explicit VmaSmallVector(const AllocatorT& allocator);+    VmaSmallVector(size_t count, const AllocatorT& allocator);+    template<typename SrcT, typename SrcAllocatorT, size_t SrcN>+    explicit VmaSmallVector(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;+    template<typename SrcT, typename SrcAllocatorT, size_t SrcN>+    VmaSmallVector<T, AllocatorT, N>& operator=(const VmaSmallVector<SrcT, SrcAllocatorT, SrcN>&) = delete;+    ~VmaSmallVector() = default;++    bool empty() const { return m_Count == 0; }+    size_t size() const { return m_Count; }+    T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }+    T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }+    T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }+    const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; }+    const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; }+    const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; }++    iterator begin() { return data(); }+    iterator end() { return data() + m_Count; }++    void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); }+    void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); }+    void push_front(const T& src) { insert(0, src); }++    void push_back(const T& src);+    void resize(size_t newCount, bool freeMemory = false);+    void clear(bool freeMemory = false);+    void insert(size_t index, const T& src);+    void remove(size_t index);++    T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }+    const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; }++private:+    size_t m_Count;+    T m_StaticArray[N]; // Used when m_Size <= N+    VmaVector<T, AllocatorT> m_DynamicArray; // Used when m_Size > N+};++#ifndef _VMA_SMALL_VECTOR_FUNCTIONS+template<typename T, typename AllocatorT, size_t N>+VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(const AllocatorT& allocator)+    : m_Count(0),+    m_DynamicArray(allocator) {}++template<typename T, typename AllocatorT, size_t N>+VmaSmallVector<T, AllocatorT, N>::VmaSmallVector(size_t count, const AllocatorT& allocator)+    : m_Count(count),+    m_DynamicArray(count > N ? count : 0, allocator) {}++template<typename T, typename AllocatorT, size_t N>+void VmaSmallVector<T, AllocatorT, N>::push_back(const T& src)+{+    const size_t newIndex = size();+    resize(newIndex + 1);+    data()[newIndex] = src;+}++template<typename T, typename AllocatorT, size_t N>+void VmaSmallVector<T, AllocatorT, N>::resize(size_t newCount, bool freeMemory)+{+    if (newCount > N && m_Count > N)+    {+        // Any direction, staying in m_DynamicArray+        m_DynamicArray.resize(newCount);+        if (freeMemory)+        {+            m_DynamicArray.shrink_to_fit();+        }+    }+    else if (newCount > N && m_Count <= N)+    {+        // Growing, moving from m_StaticArray to m_DynamicArray+        m_DynamicArray.resize(newCount);+        if (m_Count > 0)+        {+            memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T));+        }+    }+    else if (newCount <= N && m_Count > N)+    {+        // Shrinking, moving from m_DynamicArray to m_StaticArray+        if (newCount > 0)+        {+            memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T));+        }+        m_DynamicArray.resize(0);+        if (freeMemory)+        {+            m_DynamicArray.shrink_to_fit();+        }+    }+    else+    {+        // Any direction, staying in m_StaticArray - nothing to do here+    }+    m_Count = newCount;+}++template<typename T, typename AllocatorT, size_t N>+void VmaSmallVector<T, AllocatorT, N>::clear(bool freeMemory)+{+    m_DynamicArray.clear();+    if (freeMemory)+    {+        m_DynamicArray.shrink_to_fit();+    }+    m_Count = 0;+}++template<typename T, typename AllocatorT, size_t N>+void VmaSmallVector<T, AllocatorT, N>::insert(size_t index, const T& src)+{+    VMA_HEAVY_ASSERT(index <= m_Count);+    const size_t oldCount = size();+    resize(oldCount + 1);+    T* const dataPtr = data();+    if (index < oldCount)+    {+        //  I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray.+        memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T));+    }+    dataPtr[index] = src;+}++template<typename T, typename AllocatorT, size_t N>+void VmaSmallVector<T, AllocatorT, N>::remove(size_t index)+{+    VMA_HEAVY_ASSERT(index < m_Count);+    const size_t oldCount = size();+    if (index < oldCount - 1)+    {+        //  I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray.+        T* const dataPtr = data();+        memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T));+    }+    resize(oldCount - 1);+}+#endif // _VMA_SMALL_VECTOR_FUNCTIONS+#endif // _VMA_SMALL_VECTOR++#ifndef _VMA_POOL_ALLOCATOR+/*+Allocator for objects of type T using a list of arrays (pools) to speed up+allocation. Number of elements that can be allocated is not bounded because+allocator can create multiple blocks.+*/+template<typename T>+class VmaPoolAllocator+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaPoolAllocator)+public:+    VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity);+    ~VmaPoolAllocator();+    template<typename... Types> T* Alloc(Types&&... args);+    void Free(T* ptr);++private:+    union Item+    {+        uint32_t NextFreeIndex;+        alignas(T) char Value[sizeof(T)];+    };+    struct ItemBlock+    {+        Item* pItems;+        uint32_t Capacity;+        uint32_t FirstFreeIndex;+    };++    const VkAllocationCallbacks* m_pAllocationCallbacks;+    const uint32_t m_FirstBlockCapacity;+    VmaVector<ItemBlock, VmaStlAllocator<ItemBlock>> m_ItemBlocks;++    ItemBlock& CreateNewBlock();+};++#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS+template<typename T>+VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity)+    : m_pAllocationCallbacks(pAllocationCallbacks),+    m_FirstBlockCapacity(firstBlockCapacity),+    m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))+{+    VMA_ASSERT(m_FirstBlockCapacity > 1);+}++template<typename T>+VmaPoolAllocator<T>::~VmaPoolAllocator()+{+    for (size_t i = m_ItemBlocks.size(); i--;)+        vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity);+    m_ItemBlocks.clear();+}++template<typename T>+template<typename... Types> T* VmaPoolAllocator<T>::Alloc(Types&&... args)+{+    for (size_t i = m_ItemBlocks.size(); i--; )+    {+        ItemBlock& block = m_ItemBlocks[i];+        // This block has some free items: Use first one.+        if (block.FirstFreeIndex != UINT32_MAX)+        {+            Item* const pItem = &block.pItems[block.FirstFreeIndex];+            block.FirstFreeIndex = pItem->NextFreeIndex;+            T* result = (T*)&pItem->Value;+            new(result)T(std::forward<Types>(args)...); // Explicit constructor call.+            return result;+        }+    }++    // No block has free item: Create new one and use it.+    ItemBlock& newBlock = CreateNewBlock();+    Item* const pItem = &newBlock.pItems[0];+    newBlock.FirstFreeIndex = pItem->NextFreeIndex;+    T* result = (T*)&pItem->Value;+    new(result) T(std::forward<Types>(args)...); // Explicit constructor call.+    return result;+}++template<typename T>+void VmaPoolAllocator<T>::Free(T* ptr)+{+    // Search all memory blocks to find ptr.+    for (size_t i = m_ItemBlocks.size(); i--; )+    {+        ItemBlock& block = m_ItemBlocks[i];++        // Casting to union.+        Item* pItemPtr = VMA_NULL;+        memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));++        // Check if pItemPtr is in address range of this block.+        if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity))+        {+            ptr->~T(); // Explicit destructor call.+            const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);+            pItemPtr->NextFreeIndex = block.FirstFreeIndex;+            block.FirstFreeIndex = index;+            return;+        }+    }+    VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");+}++template<typename T>+typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()+{+    const uint32_t newBlockCapacity = m_ItemBlocks.empty() ?+        m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2;++    const ItemBlock newBlock =+    {+        vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity),+        newBlockCapacity,+        0+    };++    m_ItemBlocks.push_back(newBlock);++    // Setup singly-linked list of all free items in this block.+    for (uint32_t i = 0; i < newBlockCapacity - 1; ++i)+        newBlock.pItems[i].NextFreeIndex = i + 1;+    newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX;+    return m_ItemBlocks.back();+}+#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS+#endif // _VMA_POOL_ALLOCATOR++#ifndef _VMA_RAW_LIST+template<typename T>+struct VmaListItem+{+    VmaListItem* pPrev;+    VmaListItem* pNext;+    T Value;+};++// Doubly linked list.+template<typename T>+class VmaRawList+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaRawList)+public:+    typedef VmaListItem<T> ItemType;++    explicit VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);+    // Intentionally not calling Clear, because that would be unnecessary+    // computations to return all items to m_ItemAllocator as free.+    ~VmaRawList() = default;++    size_t GetCount() const { return m_Count; }+    bool IsEmpty() const { return m_Count == 0; }++    ItemType* Front() { return m_pFront; }+    ItemType* Back() { return m_pBack; }+    const ItemType* Front() const { return m_pFront; }+    const ItemType* Back() const { return m_pBack; }++    ItemType* PushFront();+    ItemType* PushBack();+    ItemType* PushFront(const T& value);+    ItemType* PushBack(const T& value);+    void PopFront();+    void PopBack();++    // Item can be null - it means PushBack.+    ItemType* InsertBefore(ItemType* pItem);+    // Item can be null - it means PushFront.+    ItemType* InsertAfter(ItemType* pItem);+    ItemType* InsertBefore(ItemType* pItem, const T& value);+    ItemType* InsertAfter(ItemType* pItem, const T& value);++    void Clear();+    void Remove(ItemType* pItem);++private:+    const VkAllocationCallbacks* const m_pAllocationCallbacks;+    VmaPoolAllocator<ItemType> m_ItemAllocator;+    ItemType* m_pFront;+    ItemType* m_pBack;+    size_t m_Count;+};++#ifndef _VMA_RAW_LIST_FUNCTIONS+template<typename T>+VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks)+    : m_pAllocationCallbacks(pAllocationCallbacks),+    m_ItemAllocator(pAllocationCallbacks, 128),+    m_pFront(VMA_NULL),+    m_pBack(VMA_NULL),+    m_Count(0) {}++template<typename T>+VmaListItem<T>* VmaRawList<T>::PushFront()+{+    ItemType* const pNewItem = m_ItemAllocator.Alloc();+    pNewItem->pPrev = VMA_NULL;+    if (IsEmpty())+    {+        pNewItem->pNext = VMA_NULL;+        m_pFront = pNewItem;+        m_pBack = pNewItem;+        m_Count = 1;+    }+    else+    {+        pNewItem->pNext = m_pFront;+        m_pFront->pPrev = pNewItem;+        m_pFront = pNewItem;+        ++m_Count;+    }+    return pNewItem;+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::PushBack()+{+    ItemType* const pNewItem = m_ItemAllocator.Alloc();+    pNewItem->pNext = VMA_NULL;+    if(IsEmpty())+    {+        pNewItem->pPrev = VMA_NULL;+        m_pFront = pNewItem;+        m_pBack = pNewItem;+        m_Count = 1;+    }+    else+    {+        pNewItem->pPrev = m_pBack;+        m_pBack->pNext = pNewItem;+        m_pBack = pNewItem;+        ++m_Count;+    }+    return pNewItem;+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)+{+    ItemType* const pNewItem = PushFront();+    pNewItem->Value = value;+    return pNewItem;+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)+{+    ItemType* const pNewItem = PushBack();+    pNewItem->Value = value;+    return pNewItem;+}++template<typename T>+void VmaRawList<T>::PopFront()+{+    VMA_HEAVY_ASSERT(m_Count > 0);+    ItemType* const pFrontItem = m_pFront;+    ItemType* const pNextItem = pFrontItem->pNext;+    if (pNextItem != VMA_NULL)+    {+        pNextItem->pPrev = VMA_NULL;+    }+    m_pFront = pNextItem;+    m_ItemAllocator.Free(pFrontItem);+    --m_Count;+}++template<typename T>+void VmaRawList<T>::PopBack()+{+    VMA_HEAVY_ASSERT(m_Count > 0);+    ItemType* const pBackItem = m_pBack;+    ItemType* const pPrevItem = pBackItem->pPrev;+    if(pPrevItem != VMA_NULL)+    {+        pPrevItem->pNext = VMA_NULL;+    }+    m_pBack = pPrevItem;+    m_ItemAllocator.Free(pBackItem);+    --m_Count;+}++template<typename T>+void VmaRawList<T>::Clear()+{+    if (!IsEmpty())+    {+        ItemType* pItem = m_pBack;+        while (pItem != VMA_NULL)+        {+            ItemType* const pPrevItem = pItem->pPrev;+            m_ItemAllocator.Free(pItem);+            pItem = pPrevItem;+        }+        m_pFront = VMA_NULL;+        m_pBack = VMA_NULL;+        m_Count = 0;+    }+}++template<typename T>+void VmaRawList<T>::Remove(ItemType* pItem)+{+    VMA_HEAVY_ASSERT(pItem != VMA_NULL);+    VMA_HEAVY_ASSERT(m_Count > 0);++    if(pItem->pPrev != VMA_NULL)+    {+        pItem->pPrev->pNext = pItem->pNext;+    }+    else+    {+        VMA_HEAVY_ASSERT(m_pFront == pItem);+        m_pFront = pItem->pNext;+    }++    if(pItem->pNext != VMA_NULL)+    {+        pItem->pNext->pPrev = pItem->pPrev;+    }+    else+    {+        VMA_HEAVY_ASSERT(m_pBack == pItem);+        m_pBack = pItem->pPrev;+    }++    m_ItemAllocator.Free(pItem);+    --m_Count;+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)+{+    if(pItem != VMA_NULL)+    {+        ItemType* const prevItem = pItem->pPrev;+        ItemType* const newItem = m_ItemAllocator.Alloc();+        newItem->pPrev = prevItem;+        newItem->pNext = pItem;+        pItem->pPrev = newItem;+        if(prevItem != VMA_NULL)+        {+            prevItem->pNext = newItem;+        }+        else+        {+            VMA_HEAVY_ASSERT(m_pFront == pItem);+            m_pFront = newItem;+        }+        ++m_Count;+        return newItem;+    }+    return PushBack();+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)+{+    if(pItem != VMA_NULL)+    {+        ItemType* const nextItem = pItem->pNext;+        ItemType* const newItem = m_ItemAllocator.Alloc();+        newItem->pNext = nextItem;+        newItem->pPrev = pItem;+        pItem->pNext = newItem;+        if(nextItem != VMA_NULL)+        {+            nextItem->pPrev = newItem;+        }+        else+        {+            VMA_HEAVY_ASSERT(m_pBack == pItem);+            m_pBack = newItem;+        }+        ++m_Count;+        return newItem;+    }+    return PushFront();+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)+{+    ItemType* const newItem = InsertBefore(pItem);+    newItem->Value = value;+    return newItem;+}++template<typename T>+VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)+{+    ItemType* const newItem = InsertAfter(pItem);+    newItem->Value = value;+    return newItem;+}+#endif // _VMA_RAW_LIST_FUNCTIONS+#endif // _VMA_RAW_LIST++#ifndef _VMA_LIST+template<typename T, typename AllocatorT>+class VmaList+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaList)+public:+    class reverse_iterator;+    class const_iterator;+    class const_reverse_iterator;++    class iterator+    {+        friend class const_iterator;+        friend class VmaList<T, AllocatorT>;+    public:+        iterator() :  m_pList(VMA_NULL), m_pItem(VMA_NULL) {}+        explicit iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}++        T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }+        T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }++        bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }+        bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }++        const iterator operator++(int) { iterator result = *this; ++*this; return result; }+        const iterator operator--(int) { iterator result = *this; --*this; return result; }++        iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }+        iterator& operator--();++    private:+        VmaRawList<T>* m_pList;+        VmaListItem<T>* m_pItem;++        iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}+    };+    class reverse_iterator+    {+        friend class const_reverse_iterator;+        friend class VmaList<T, AllocatorT>;+    public:+        reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}+        explicit reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}++        T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }+        T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }++        bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }+        bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }++        const reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; }+        const reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; }++        reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }+        reverse_iterator& operator--();++    private:+        VmaRawList<T>* m_pList;+        VmaListItem<T>* m_pItem;++        reverse_iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) : m_pList(pList),  m_pItem(pItem) {}+    };+    class const_iterator+    {+        friend class VmaList<T, AllocatorT>;+    public:+        const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}+        explicit const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}+        explicit const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}++        iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }++        const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }+        const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }++        bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }+        bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }++        const const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; }+        const const_iterator operator--(int) { const_iterator result = *this; --* this; return result; }++        const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; }+        const_iterator& operator--();++    private:+        const VmaRawList<T>* m_pList;+        const VmaListItem<T>* m_pItem;++        const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}+    };+    class const_reverse_iterator+    {+        friend class VmaList<T, AllocatorT>;+    public:+        const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {}+        explicit const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}+        explicit const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {}++        reverse_iterator drop_const() { return { const_cast<VmaRawList<T>*>(m_pList), const_cast<VmaListItem<T>*>(m_pItem) }; }++        const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; }+        const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; }++        bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; }+        bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; }++        const const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; }+        const const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; }++        const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; }+        const_reverse_iterator& operator--();++    private:+        const VmaRawList<T>* m_pList;+        const VmaListItem<T>* m_pItem;++        const_reverse_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) : m_pList(pList), m_pItem(pItem) {}+    };++    explicit VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {}++    bool empty() const { return m_RawList.IsEmpty(); }+    size_t size() const { return m_RawList.GetCount(); }++    iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }+    iterator end() { return iterator(&m_RawList, VMA_NULL); }++    const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }+    const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }++    const_iterator begin() const { return cbegin(); }+    const_iterator end() const { return cend(); }++    reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); }+    reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); }++    const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); }+    const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); }++    const_reverse_iterator rbegin() const { return crbegin(); }+    const_reverse_iterator rend() const { return crend(); }++    void push_back(const T& value) { m_RawList.PushBack(value); }+    iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }++    void clear() { m_RawList.Clear(); }+    void erase(iterator it) { m_RawList.Remove(it.m_pItem); }++private:+    VmaRawList<T> m_RawList;+};++#ifndef _VMA_LIST_FUNCTIONS+template<typename T, typename AllocatorT>+typename VmaList<T, AllocatorT>::iterator& VmaList<T, AllocatorT>::iterator::operator--()+{+    if (m_pItem != VMA_NULL)+    {+        m_pItem = m_pItem->pPrev;+    }+    else+    {+        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());+        m_pItem = m_pList->Back();+    }+    return *this;+}++template<typename T, typename AllocatorT>+typename VmaList<T, AllocatorT>::reverse_iterator& VmaList<T, AllocatorT>::reverse_iterator::operator--()+{+    if (m_pItem != VMA_NULL)+    {+        m_pItem = m_pItem->pNext;+    }+    else+    {+        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());+        m_pItem = m_pList->Front();+    }+    return *this;+}++template<typename T, typename AllocatorT>+typename VmaList<T, AllocatorT>::const_iterator& VmaList<T, AllocatorT>::const_iterator::operator--()+{+    if (m_pItem != VMA_NULL)+    {+        m_pItem = m_pItem->pPrev;+    }+    else+    {+        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());+        m_pItem = m_pList->Back();+    }+    return *this;+}++template<typename T, typename AllocatorT>+typename VmaList<T, AllocatorT>::const_reverse_iterator& VmaList<T, AllocatorT>::const_reverse_iterator::operator--()+{+    if (m_pItem != VMA_NULL)+    {+        m_pItem = m_pItem->pNext;+    }+    else+    {+        VMA_HEAVY_ASSERT(!m_pList->IsEmpty());+        m_pItem = m_pList->Back();+    }+    return *this;+}+#endif // _VMA_LIST_FUNCTIONS+#endif // _VMA_LIST++#ifndef _VMA_INTRUSIVE_LINKED_LIST+/*+Expected interface of ItemTypeTraits:+struct MyItemTypeTraits+{+    typedef MyItem ItemType;+    static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; }+    static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; }+    static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; }+    static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; }+};+*/+template<typename ItemTypeTraits>+class VmaIntrusiveLinkedList+{+public:+    typedef typename ItemTypeTraits::ItemType ItemType;+    static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); }+    static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); }++    // Movable, not copyable.+    VmaIntrusiveLinkedList() = default;+    VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src) noexcept;+    VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete;+    VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src) noexcept;+    VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete;+    ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); }++    size_t GetCount() const { return m_Count; }+    bool IsEmpty() const { return m_Count == 0; }+    ItemType* Front() { return m_Front; }+    ItemType* Back() { return m_Back; }+    const ItemType* Front() const { return m_Front; }+    const ItemType* Back() const { return m_Back; }++    void PushBack(ItemType* item);+    void PushFront(ItemType* item);+    ItemType* PopBack();+    ItemType* PopFront();++    // MyItem can be null - it means PushBack.+    void InsertBefore(ItemType* existingItem, ItemType* newItem);+    // MyItem can be null - it means PushFront.+    void InsertAfter(ItemType* existingItem, ItemType* newItem);+    void Remove(ItemType* item);+    void RemoveAll();++private:+    ItemType* m_Front = VMA_NULL;+    ItemType* m_Back = VMA_NULL;+    size_t m_Count = 0;+};++#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS+template<typename ItemTypeTraits>+VmaIntrusiveLinkedList<ItemTypeTraits>::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src) noexcept+    : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count)+{+    src.m_Front = src.m_Back = VMA_NULL;+    src.m_Count = 0;+}++template<typename ItemTypeTraits>+VmaIntrusiveLinkedList<ItemTypeTraits>& VmaIntrusiveLinkedList<ItemTypeTraits>::operator=(VmaIntrusiveLinkedList&& src) noexcept+{+    if (&src != this)+    {+        VMA_HEAVY_ASSERT(IsEmpty());+        m_Front = src.m_Front;+        m_Back = src.m_Back;+        m_Count = src.m_Count;+        src.m_Front = src.m_Back = VMA_NULL;+        src.m_Count = 0;+    }+    return *this;+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::PushBack(ItemType* item)+{+    VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);+    if (IsEmpty())+    {+        m_Front = item;+        m_Back = item;+        m_Count = 1;+    }+    else+    {+        ItemTypeTraits::AccessPrev(item) = m_Back;+        ItemTypeTraits::AccessNext(m_Back) = item;+        m_Back = item;+        ++m_Count;+    }+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::PushFront(ItemType* item)+{+    VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL);+    if (IsEmpty())+    {+        m_Front = item;+        m_Back = item;+        m_Count = 1;+    }+    else+    {+        ItemTypeTraits::AccessNext(item) = m_Front;+        ItemTypeTraits::AccessPrev(m_Front) = item;+        m_Front = item;+        ++m_Count;+    }+}++template<typename ItemTypeTraits>+typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopBack()+{+    VMA_HEAVY_ASSERT(m_Count > 0);+    ItemType* const backItem = m_Back;+    ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem);+    if (prevItem != VMA_NULL)+    {+        ItemTypeTraits::AccessNext(prevItem) = VMA_NULL;+    }+    m_Back = prevItem;+    --m_Count;+    ItemTypeTraits::AccessPrev(backItem) = VMA_NULL;+    ItemTypeTraits::AccessNext(backItem) = VMA_NULL;+    return backItem;+}++template<typename ItemTypeTraits>+typename VmaIntrusiveLinkedList<ItemTypeTraits>::ItemType* VmaIntrusiveLinkedList<ItemTypeTraits>::PopFront()+{+    VMA_HEAVY_ASSERT(m_Count > 0);+    ItemType* const frontItem = m_Front;+    ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem);+    if (nextItem != VMA_NULL)+    {+        ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL;+    }+    m_Front = nextItem;+    --m_Count;+    ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL;+    ItemTypeTraits::AccessNext(frontItem) = VMA_NULL;+    return frontItem;+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertBefore(ItemType* existingItem, ItemType* newItem)+{+    VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);+    if (existingItem != VMA_NULL)+    {+        ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem);+        ItemTypeTraits::AccessPrev(newItem) = prevItem;+        ItemTypeTraits::AccessNext(newItem) = existingItem;+        ItemTypeTraits::AccessPrev(existingItem) = newItem;+        if (prevItem != VMA_NULL)+        {+            ItemTypeTraits::AccessNext(prevItem) = newItem;+        }+        else+        {+            VMA_HEAVY_ASSERT(m_Front == existingItem);+            m_Front = newItem;+        }+        ++m_Count;+    }+    else+        PushBack(newItem);+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::InsertAfter(ItemType* existingItem, ItemType* newItem)+{+    VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL);+    if (existingItem != VMA_NULL)+    {+        ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem);+        ItemTypeTraits::AccessNext(newItem) = nextItem;+        ItemTypeTraits::AccessPrev(newItem) = existingItem;+        ItemTypeTraits::AccessNext(existingItem) = newItem;+        if (nextItem != VMA_NULL)+        {+            ItemTypeTraits::AccessPrev(nextItem) = newItem;+        }+        else+        {+            VMA_HEAVY_ASSERT(m_Back == existingItem);+            m_Back = newItem;+        }+        ++m_Count;+    }+    else+        return PushFront(newItem);+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::Remove(ItemType* item)+{+    VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0);+    if (ItemTypeTraits::GetPrev(item) != VMA_NULL)+    {+        ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item);+    }+    else+    {+        VMA_HEAVY_ASSERT(m_Front == item);+        m_Front = ItemTypeTraits::GetNext(item);+    }++    if (ItemTypeTraits::GetNext(item) != VMA_NULL)+    {+        ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item);+    }+    else+    {+        VMA_HEAVY_ASSERT(m_Back == item);+        m_Back = ItemTypeTraits::GetPrev(item);+    }+    ItemTypeTraits::AccessPrev(item) = VMA_NULL;+    ItemTypeTraits::AccessNext(item) = VMA_NULL;+    --m_Count;+}++template<typename ItemTypeTraits>+void VmaIntrusiveLinkedList<ItemTypeTraits>::RemoveAll()+{+    if (!IsEmpty())+    {+        ItemType* item = m_Back;+        while (item != VMA_NULL)+        {+            ItemType* const prevItem = ItemTypeTraits::AccessPrev(item);+            ItemTypeTraits::AccessPrev(item) = VMA_NULL;+            ItemTypeTraits::AccessNext(item) = VMA_NULL;+            item = prevItem;+        }+        m_Front = VMA_NULL;+        m_Back = VMA_NULL;+        m_Count = 0;+    }+}+#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS+#endif // _VMA_INTRUSIVE_LINKED_LIST++#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED+class VmaStringBuilder+{+public:+    explicit VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator<char>(allocationCallbacks)) {}+    ~VmaStringBuilder() = default;++    size_t GetLength() const { return m_Data.size(); }+    // Returned string is not null-terminated!+    const char* GetData() const { return m_Data.data(); }+    void AddNewLine() { Add('\n'); }+    void Add(char ch) { m_Data.push_back(ch); }++    void Add(const char* pStr);+    void AddNumber(uint32_t num);+    void AddNumber(uint64_t num);+    void AddPointer(const void* ptr);++private:+    VmaVector<char, VmaStlAllocator<char>> m_Data;+};++#ifndef _VMA_STRING_BUILDER_FUNCTIONS+void VmaStringBuilder::Add(const char* pStr)+{+    const size_t strLen = strlen(pStr);+    if (strLen > 0)+    {+        const size_t oldCount = m_Data.size();+        m_Data.resize(oldCount + strLen);+        memcpy(m_Data.data() + oldCount, pStr, strLen);+    }+}++void VmaStringBuilder::AddNumber(uint32_t num)+{+    char buf[11];+    buf[10] = '\0';+    char* p = &buf[10];+    do+    {+        *--p = '0' + (char)(num % 10);+        num /= 10;+    } while (num);+    Add(p);+}++void VmaStringBuilder::AddNumber(uint64_t num)+{+    char buf[21];+    buf[20] = '\0';+    char* p = &buf[20];+    do+    {+        *--p = '0' + (char)(num % 10);+        num /= 10;+    } while (num);+    Add(p);+}++void VmaStringBuilder::AddPointer(const void* ptr)+{+    char buf[21];+    VmaPtrToStr(buf, sizeof(buf), ptr);+    Add(buf);+}+#endif //_VMA_STRING_BUILDER_FUNCTIONS+#endif // _VMA_STRING_BUILDER++#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED+/*+Allows to conveniently build a correct JSON document to be written to the+VmaStringBuilder passed to the constructor.+*/+class VmaJsonWriter+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaJsonWriter)+public:+    // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object.+    VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb);+    ~VmaJsonWriter();++    // Begins object by writing "{".+    // Inside an object, you must call pairs of WriteString and a value, e.g.:+    // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject();+    // Will write: { "A": 1, "B": 2 }+    void BeginObject(bool singleLine = false);+    // Ends object by writing "}".+    void EndObject();++    // Begins array by writing "[".+    // Inside an array, you can write a sequence of any values.+    void BeginArray(bool singleLine = false);+    // Ends array by writing "[".+    void EndArray();++    // Writes a string value inside "".+    // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped.+    void WriteString(const char* pStr);++    // Begins writing a string value.+    // Call BeginString, ContinueString, ContinueString, ..., EndString instead of+    // WriteString to conveniently build the string content incrementally, made of+    // parts including numbers.+    void BeginString(const char* pStr = VMA_NULL);+    // Posts next part of an open string.+    void ContinueString(const char* pStr);+    // Posts next part of an open string. The number is converted to decimal characters.+    void ContinueString(uint32_t n);+    void ContinueString(uint64_t n);+    // Posts next part of an open string. Pointer value is converted to characters+    // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00+    void ContinueString_Pointer(const void* ptr);+    // Ends writing a string value by writing '"'.+    void EndString(const char* pStr = VMA_NULL);++    // Writes a number value.+    void WriteNumber(uint32_t n);+    void WriteNumber(uint64_t n);+    // Writes a boolean value - false or true.+    void WriteBool(bool b);+    // Writes a null value.+    void WriteNull();++private:+    enum COLLECTION_TYPE+    {+        COLLECTION_TYPE_OBJECT,+        COLLECTION_TYPE_ARRAY,+    };+    struct StackItem+    {+        COLLECTION_TYPE type;+        uint32_t valueCount;+        bool singleLineMode;+    };++    static const char* const INDENT;++    VmaStringBuilder& m_SB;+    VmaVector< StackItem, VmaStlAllocator<StackItem> > m_Stack;+    bool m_InsideString;++    void BeginValue(bool isString);+    void WriteIndent(bool oneLess = false);+};+const char* const VmaJsonWriter::INDENT = "  ";++#ifndef _VMA_JSON_WRITER_FUNCTIONS+VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb)+    : m_SB(sb),+    m_Stack(VmaStlAllocator<StackItem>(pAllocationCallbacks)),+    m_InsideString(false) {}++VmaJsonWriter::~VmaJsonWriter()+{+    VMA_ASSERT(!m_InsideString);+    VMA_ASSERT(m_Stack.empty());+}++void VmaJsonWriter::BeginObject(bool singleLine)+{+    VMA_ASSERT(!m_InsideString);++    BeginValue(false);+    m_SB.Add('{');++    StackItem item;+    item.type = COLLECTION_TYPE_OBJECT;+    item.valueCount = 0;+    item.singleLineMode = singleLine;+    m_Stack.push_back(item);+}++void VmaJsonWriter::EndObject()+{+    VMA_ASSERT(!m_InsideString);++    WriteIndent(true);+    m_SB.Add('}');++    VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT);+    m_Stack.pop_back();+}++void VmaJsonWriter::BeginArray(bool singleLine)+{+    VMA_ASSERT(!m_InsideString);++    BeginValue(false);+    m_SB.Add('[');++    StackItem item;+    item.type = COLLECTION_TYPE_ARRAY;+    item.valueCount = 0;+    item.singleLineMode = singleLine;+    m_Stack.push_back(item);+}++void VmaJsonWriter::EndArray()+{+    VMA_ASSERT(!m_InsideString);++    WriteIndent(true);+    m_SB.Add(']');++    VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY);+    m_Stack.pop_back();+}++void VmaJsonWriter::WriteString(const char* pStr)+{+    BeginString(pStr);+    EndString();+}++void VmaJsonWriter::BeginString(const char* pStr)+{+    VMA_ASSERT(!m_InsideString);++    BeginValue(true);+    m_SB.Add('"');+    m_InsideString = true;+    if (pStr != VMA_NULL && pStr[0] != '\0')+    {+        ContinueString(pStr);+    }+}++void VmaJsonWriter::ContinueString(const char* pStr)+{+    VMA_ASSERT(m_InsideString);++    const size_t strLen = strlen(pStr);+    for (size_t i = 0; i < strLen; ++i)+    {+        char ch = pStr[i];+        if (ch == '\\')+        {+            m_SB.Add("\\\\");+        }+        else if (ch == '"')+        {+            m_SB.Add("\\\"");+        }+        else if ((uint8_t)ch >= 32)+        {+            m_SB.Add(ch);+        }+        else switch (ch)+        {+        case '\b':+            m_SB.Add("\\b");+            break;+        case '\f':+            m_SB.Add("\\f");+            break;+        case '\n':+            m_SB.Add("\\n");+            break;+        case '\r':+            m_SB.Add("\\r");+            break;+        case '\t':+            m_SB.Add("\\t");+            break;+        default:+            VMA_ASSERT(0 && "Character not currently supported.");+        }+    }+}++void VmaJsonWriter::ContinueString(uint32_t n)+{+    VMA_ASSERT(m_InsideString);+    m_SB.AddNumber(n);+}++void VmaJsonWriter::ContinueString(uint64_t n)+{+    VMA_ASSERT(m_InsideString);+    m_SB.AddNumber(n);+}++void VmaJsonWriter::ContinueString_Pointer(const void* ptr)+{+    VMA_ASSERT(m_InsideString);+    m_SB.AddPointer(ptr);+}++void VmaJsonWriter::EndString(const char* pStr)+{+    VMA_ASSERT(m_InsideString);+    if (pStr != VMA_NULL && pStr[0] != '\0')+    {+        ContinueString(pStr);+    }+    m_SB.Add('"');+    m_InsideString = false;+}++void VmaJsonWriter::WriteNumber(uint32_t n)+{+    VMA_ASSERT(!m_InsideString);+    BeginValue(false);+    m_SB.AddNumber(n);+}++void VmaJsonWriter::WriteNumber(uint64_t n)+{+    VMA_ASSERT(!m_InsideString);+    BeginValue(false);+    m_SB.AddNumber(n);+}++void VmaJsonWriter::WriteBool(bool b)+{+    VMA_ASSERT(!m_InsideString);+    BeginValue(false);+    m_SB.Add(b ? "true" : "false");+}++void VmaJsonWriter::WriteNull()+{+    VMA_ASSERT(!m_InsideString);+    BeginValue(false);+    m_SB.Add("null");+}++void VmaJsonWriter::BeginValue(bool isString)+{+    if (!m_Stack.empty())+    {+        StackItem& currItem = m_Stack.back();+        if (currItem.type == COLLECTION_TYPE_OBJECT &&+            currItem.valueCount % 2 == 0)+        {+            VMA_ASSERT(isString);+        }++        if (currItem.type == COLLECTION_TYPE_OBJECT &&+            currItem.valueCount % 2 != 0)+        {+            m_SB.Add(": ");+        }+        else if (currItem.valueCount > 0)+        {+            m_SB.Add(", ");+            WriteIndent();+        }+        else+        {+            WriteIndent();+        }+        ++currItem.valueCount;+    }+}++void VmaJsonWriter::WriteIndent(bool oneLess)+{+    if (!m_Stack.empty() && !m_Stack.back().singleLineMode)+    {+        m_SB.AddNewLine();++        size_t count = m_Stack.size();+        if (count > 0 && oneLess)+        {+            --count;+        }+        for (size_t i = 0; i < count; ++i)+        {+            m_SB.Add(INDENT);+        }+    }+}+#endif // _VMA_JSON_WRITER_FUNCTIONS++static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat)+{+    json.BeginObject();++    json.WriteString("BlockCount");+    json.WriteNumber(stat.statistics.blockCount);+    json.WriteString("BlockBytes");+    json.WriteNumber(stat.statistics.blockBytes);+    json.WriteString("AllocationCount");+    json.WriteNumber(stat.statistics.allocationCount);+    json.WriteString("AllocationBytes");+    json.WriteNumber(stat.statistics.allocationBytes);+    json.WriteString("UnusedRangeCount");+    json.WriteNumber(stat.unusedRangeCount);++    if (stat.statistics.allocationCount > 1)+    {+        json.WriteString("AllocationSizeMin");+        json.WriteNumber(stat.allocationSizeMin);+        json.WriteString("AllocationSizeMax");+        json.WriteNumber(stat.allocationSizeMax);+    }+    if (stat.unusedRangeCount > 1)+    {+        json.WriteString("UnusedRangeSizeMin");+        json.WriteNumber(stat.unusedRangeSizeMin);+        json.WriteString("UnusedRangeSizeMax");+        json.WriteNumber(stat.unusedRangeSizeMax);+    }+    json.EndObject();+}+#endif // _VMA_JSON_WRITER++#ifndef _VMA_MAPPING_HYSTERESIS++class VmaMappingHysteresis+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaMappingHysteresis)+public:+    VmaMappingHysteresis() = default;++    uint32_t GetExtraMapping() const { return m_ExtraMapping; }++    // Call when Map was called.+    // Returns true if switched to extra +1 mapping reference count.+    bool PostMap()+    {+#if VMA_MAPPING_HYSTERESIS_ENABLED+        if(m_ExtraMapping == 0)+        {+            ++m_MajorCounter;+            if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING)+            {+                m_ExtraMapping = 1;+                m_MajorCounter = 0;+                m_MinorCounter = 0;+                return true;+            }+        }+        else // m_ExtraMapping == 1+            PostMinorCounter();+#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED+        return false;+    }++    // Call when Unmap was called.+    void PostUnmap()+    {+#if VMA_MAPPING_HYSTERESIS_ENABLED+        if(m_ExtraMapping == 0)+            ++m_MajorCounter;+        else // m_ExtraMapping == 1+            PostMinorCounter();+#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED+    }++    // Call when allocation was made from the memory block.+    void PostAlloc()+    {+#if VMA_MAPPING_HYSTERESIS_ENABLED+        if(m_ExtraMapping == 1)+            ++m_MajorCounter;+        else // m_ExtraMapping == 0+            PostMinorCounter();+#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED+    }++    // Call when allocation was freed from the memory block.+    // Returns true if switched to extra -1 mapping reference count.+    bool PostFree()+    {+#if VMA_MAPPING_HYSTERESIS_ENABLED+        if(m_ExtraMapping == 1)+        {+            ++m_MajorCounter;+            if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING &&+                m_MajorCounter > m_MinorCounter + 1)+            {+                m_ExtraMapping = 0;+                m_MajorCounter = 0;+                m_MinorCounter = 0;+                return true;+            }+        }+        else // m_ExtraMapping == 0+            PostMinorCounter();+#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED+        return false;+    }++private:+    static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7;++    uint32_t m_MinorCounter = 0;+    uint32_t m_MajorCounter = 0;+    uint32_t m_ExtraMapping = 0; // 0 or 1.++    void PostMinorCounter()+    {+        if(m_MinorCounter < m_MajorCounter)+        {+            ++m_MinorCounter;+        }+        else if(m_MajorCounter > 0)+        {+            --m_MajorCounter;+            --m_MinorCounter;+        }+    }+};++#endif // _VMA_MAPPING_HYSTERESIS++#if VMA_EXTERNAL_MEMORY_WIN32+class VmaWin32Handle+{+public:+    VmaWin32Handle() noexcept : m_hHandle(VMA_NULL) { }+    explicit VmaWin32Handle(HANDLE hHandle) noexcept : m_hHandle(hHandle) { }+    ~VmaWin32Handle() noexcept { if (m_hHandle != VMA_NULL) { ::CloseHandle(m_hHandle); } }+    VMA_CLASS_NO_COPY_NO_MOVE(VmaWin32Handle)++public:+    // Strengthened+    VkResult GetHandle(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, bool useMutex, HANDLE* pHandle) noexcept+    {+        *pHandle = VMA_NULL;+        // Try to get handle first.+        if (m_hHandle != VMA_NULL)+        {+            *pHandle = Duplicate(hTargetProcess);+            return VK_SUCCESS;+        }++        VkResult res = VK_SUCCESS;+        // If failed, try to create it.+        {+            VmaMutexLockWrite lock(m_Mutex, useMutex);+            if (m_hHandle == VMA_NULL)+            {+                res = Create(device, memory, pvkGetMemoryWin32HandleKHR, &m_hHandle);+            }+        }++        *pHandle = Duplicate(hTargetProcess);+        return res;+    }++    operator bool() const noexcept { return m_hHandle != VMA_NULL; }+private:+    // Not atomic+    static VkResult Create(VkDevice device, VkDeviceMemory memory, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE* pHandle) noexcept+    {+        VkResult res = VK_ERROR_FEATURE_NOT_PRESENT;+        if (pvkGetMemoryWin32HandleKHR != VMA_NULL)+        {+            VkMemoryGetWin32HandleInfoKHR handleInfo{ };+            handleInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR;+            handleInfo.memory = memory;+            handleInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR;+            res = pvkGetMemoryWin32HandleKHR(device, &handleInfo, pHandle);+        }+        return res;+    }+    HANDLE Duplicate(HANDLE hTargetProcess = VMA_NULL) const noexcept+    {+        if (!m_hHandle)+            return m_hHandle;++        HANDLE hCurrentProcess = ::GetCurrentProcess();+        HANDLE hDupHandle = VMA_NULL;+        if (!::DuplicateHandle(hCurrentProcess, m_hHandle, hTargetProcess ? hTargetProcess : hCurrentProcess, &hDupHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))+        {+            VMA_ASSERT(0 && "Failed to duplicate handle.");+        }+        return hDupHandle;+    }+private:+    HANDLE m_hHandle;+    VMA_RW_MUTEX m_Mutex; // Protects access m_Handle+};+#else +class VmaWin32Handle+{+    // ABI compatibility+    void* placeholder = VMA_NULL;+    VMA_RW_MUTEX placeholder2;+};+#endif // VMA_EXTERNAL_MEMORY_WIN32+++#ifndef _VMA_DEVICE_MEMORY_BLOCK+/*+Represents a single block of device memory (`VkDeviceMemory`) with all the+data about its regions (aka suballocations, #VmaAllocation), assigned and free.++Thread-safety:+- Access to m_pMetadata must be externally synchronized.+- Map, Unmap, Bind* are synchronized internally.+*/+class VmaDeviceMemoryBlock+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaDeviceMemoryBlock)+public:+    VmaBlockMetadata* m_pMetadata;++    explicit VmaDeviceMemoryBlock(VmaAllocator hAllocator);+    ~VmaDeviceMemoryBlock();++    // Always call after construction.+    void Init(+        VmaAllocator hAllocator,+        VmaPool hParentPool,+        uint32_t newMemoryTypeIndex,+        VkDeviceMemory newMemory,+        VkDeviceSize newSize,+        uint32_t id,+        uint32_t algorithm,+        VkDeviceSize bufferImageGranularity);+    // Always call before destruction.+    void Destroy(VmaAllocator allocator);++    VmaPool GetParentPool() const { return m_hParentPool; }+    VkDeviceMemory GetDeviceMemory() const { return m_hMemory; }+    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }+    uint32_t GetId() const { return m_Id; }+    void* GetMappedData() const { return m_pMappedData; }+    uint32_t GetMapRefCount() const { return m_MapCount; }++    // Call when allocation/free was made from m_pMetadata.+    // Used for m_MappingHysteresis.+    void PostAlloc(VmaAllocator hAllocator);+    void PostFree(VmaAllocator hAllocator);++    // Validates all data structures inside this object. If not valid, returns false.+    bool Validate() const;+    VkResult CheckCorruption(VmaAllocator hAllocator);++    // ppData can be null.+    VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData);+    void Unmap(VmaAllocator hAllocator, uint32_t count);++    VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);+    VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize);++    VkResult BindBufferMemory(+        VmaAllocator hAllocator,+        VmaAllocation hAllocation,+        VkDeviceSize allocationLocalOffset,+        VkBuffer hBuffer,+        const void* pNext);+    VkResult BindImageMemory(+        VmaAllocator hAllocator,+        VmaAllocation hAllocation,+        VkDeviceSize allocationLocalOffset,+        VkImage hImage,+        const void* pNext);+#if VMA_EXTERNAL_MEMORY_WIN32+    VkResult CreateWin32Handle(+        const VmaAllocator hAllocator,+        PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR,+        HANDLE hTargetProcess,+        HANDLE* pHandle)noexcept;+#endif // VMA_EXTERNAL_MEMORY_WIN32+private:+    VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.+    uint32_t m_MemoryTypeIndex;+    uint32_t m_Id;+    VkDeviceMemory m_hMemory;++    /*+    Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory.+    Also protects m_MapCount, m_pMappedData.+    Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex.+    */+    VMA_MUTEX m_MapAndBindMutex;+    VmaMappingHysteresis m_MappingHysteresis;+    uint32_t m_MapCount;+    void* m_pMappedData;++    VmaWin32Handle m_Handle;+};+#endif // _VMA_DEVICE_MEMORY_BLOCK++#ifndef _VMA_ALLOCATION_T+struct VmaAllocationExtraData+{+    void* m_pMappedData = VMA_NULL; // Not null means memory is mapped.+    VmaWin32Handle m_Handle;+};++struct VmaAllocation_T+{+    friend struct VmaDedicatedAllocationListItemTraits;++    enum FLAGS+    {+        FLAG_PERSISTENT_MAP   = 0x01,+        FLAG_MAPPING_ALLOWED  = 0x02,+    };++public:+    enum ALLOCATION_TYPE+    {+        ALLOCATION_TYPE_NONE,+        ALLOCATION_TYPE_BLOCK,+        ALLOCATION_TYPE_DEDICATED,+    };++    // This struct is allocated using VmaPoolAllocator.+    explicit VmaAllocation_T(bool mappingAllowed);+    ~VmaAllocation_T();++    void InitBlockAllocation(+        VmaDeviceMemoryBlock* block,+        VmaAllocHandle allocHandle,+        VkDeviceSize alignment,+        VkDeviceSize size,+        uint32_t memoryTypeIndex,+        VmaSuballocationType suballocationType,+        bool mapped);+    // pMappedData not null means allocation is created with MAPPED flag.+    void InitDedicatedAllocation(+        VmaAllocator allocator,+        VmaPool hParentPool,+        uint32_t memoryTypeIndex,+        VkDeviceMemory hMemory,+        VmaSuballocationType suballocationType,+        void* pMappedData,+        VkDeviceSize size);+    void Destroy(VmaAllocator allocator);++    ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; }+    VkDeviceSize GetAlignment() const { return m_Alignment; }+    VkDeviceSize GetSize() const { return m_Size; }+    void* GetUserData() const { return m_pUserData; }+    const char* GetName() const { return m_pName; }+    VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; }++    VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; }+    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }+    bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; }+    bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; }++    void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; }+    void SetName(VmaAllocator hAllocator, const char* pName);+    void FreeName(VmaAllocator hAllocator);+    uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation);+    VmaAllocHandle GetAllocHandle() const;+    VkDeviceSize GetOffset() const;+    VmaPool GetParentPool() const;+    VkDeviceMemory GetMemory() const;+    void* GetMappedData() const;++    void BlockAllocMap();+    void BlockAllocUnmap();+    VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData);+    void DedicatedAllocUnmap(VmaAllocator hAllocator);++#if VMA_STATS_STRING_ENABLED+    VmaBufferImageUsage GetBufferImageUsage() const { return m_BufferImageUsage; }+    void InitBufferUsage(const VkBufferCreateInfo &createInfo, bool useKhrMaintenance5)+    {+        VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN);+        m_BufferImageUsage = VmaBufferImageUsage(createInfo, useKhrMaintenance5);+    }+    void InitImageUsage(const VkImageCreateInfo &createInfo)+    {+        VMA_ASSERT(m_BufferImageUsage == VmaBufferImageUsage::UNKNOWN);+        m_BufferImageUsage = VmaBufferImageUsage(createInfo);+    }+    void PrintParameters(class VmaJsonWriter& json) const;+#endif++#if VMA_EXTERNAL_MEMORY_WIN32+    VkResult GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* hHandle) noexcept;+#endif // VMA_EXTERNAL_MEMORY_WIN32++private:+    // Allocation out of VmaDeviceMemoryBlock.+    struct BlockAllocation+    {+        VmaDeviceMemoryBlock* m_Block;+        VmaAllocHandle m_AllocHandle;+    };+    // Allocation for an object that has its own private VkDeviceMemory.+    struct DedicatedAllocation+    {+        VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool.+        VkDeviceMemory m_hMemory;+        VmaAllocationExtraData* m_ExtraData;+        VmaAllocation_T* m_Prev;+        VmaAllocation_T* m_Next;+    };+    union+    {+        // Allocation out of VmaDeviceMemoryBlock.+        BlockAllocation m_BlockAllocation;+        // Allocation for an object that has its own private VkDeviceMemory.+        DedicatedAllocation m_DedicatedAllocation;+    };++    VkDeviceSize m_Alignment;+    VkDeviceSize m_Size;+    void* m_pUserData;+    char* m_pName;+    uint32_t m_MemoryTypeIndex;+    uint8_t m_Type; // ALLOCATION_TYPE+    uint8_t m_SuballocationType; // VmaSuballocationType+    // Reference counter for vmaMapMemory()/vmaUnmapMemory().+    uint8_t m_MapCount;+    uint8_t m_Flags; // enum FLAGS+#if VMA_STATS_STRING_ENABLED+    VmaBufferImageUsage m_BufferImageUsage; // 0 if unknown.+#endif++    void EnsureExtraData(VmaAllocator hAllocator);+};+#endif // _VMA_ALLOCATION_T++#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS+struct VmaDedicatedAllocationListItemTraits+{+    typedef VmaAllocation_T ItemType;++    static ItemType* GetPrev(const ItemType* item)+    {+        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);+        return item->m_DedicatedAllocation.m_Prev;+    }+    static ItemType* GetNext(const ItemType* item)+    {+        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);+        return item->m_DedicatedAllocation.m_Next;+    }+    static ItemType*& AccessPrev(ItemType* item)+    {+        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);+        return item->m_DedicatedAllocation.m_Prev;+    }+    static ItemType*& AccessNext(ItemType* item)+    {+        VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);+        return item->m_DedicatedAllocation.m_Next;+    }+};+#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS++#ifndef _VMA_DEDICATED_ALLOCATION_LIST+/*+Stores linked list of VmaAllocation_T objects.+Thread-safe, synchronized internally.+*/+class VmaDedicatedAllocationList+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaDedicatedAllocationList)+public:+    VmaDedicatedAllocationList() = default;+    ~VmaDedicatedAllocationList();++    void Init(bool useMutex) { m_UseMutex = useMutex; }+    bool Validate();++    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);+    void AddStatistics(VmaStatistics& inoutStats);+#if VMA_STATS_STRING_ENABLED+    // Writes JSON array with the list of allocations.+    void BuildStatsString(VmaJsonWriter& json);+#endif++    bool IsEmpty();+    void Register(VmaAllocation alloc);+    void Unregister(VmaAllocation alloc);++private:+    typedef VmaIntrusiveLinkedList<VmaDedicatedAllocationListItemTraits> DedicatedAllocationLinkedList;++    bool m_UseMutex = true;+    VMA_RW_MUTEX m_Mutex;+    DedicatedAllocationLinkedList m_AllocationList;+};++#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS++VmaDedicatedAllocationList::~VmaDedicatedAllocationList()+{+    VMA_HEAVY_ASSERT(Validate());++    if (!m_AllocationList.IsEmpty())+    {+        VMA_ASSERT_LEAK(false && "Unfreed dedicated allocations found!");+    }+}++bool VmaDedicatedAllocationList::Validate()+{+    const size_t declaredCount = m_AllocationList.GetCount();+    size_t actualCount = 0;+    VmaMutexLockRead lock(m_Mutex, m_UseMutex);+    for (VmaAllocation alloc = m_AllocationList.Front();+        alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))+    {+        ++actualCount;+    }+    VMA_VALIDATE(actualCount == declaredCount);++    return true;+}++void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)+{+    for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))+    {+        const VkDeviceSize size = item->GetSize();+        inoutStats.statistics.blockCount++;+        inoutStats.statistics.blockBytes += size;+        VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize());+    }+}++void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)+{+    VmaMutexLockRead lock(m_Mutex, m_UseMutex);++    const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount();+    inoutStats.blockCount += allocCount;+    inoutStats.allocationCount += allocCount;++    for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))+    {+        const VkDeviceSize size = item->GetSize();+        inoutStats.blockBytes += size;+        inoutStats.allocationBytes += size;+    }+}++#if VMA_STATS_STRING_ENABLED+void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json)+{+    VmaMutexLockRead lock(m_Mutex, m_UseMutex);+    json.BeginArray();+    for (VmaAllocation alloc = m_AllocationList.Front();+        alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc))+    {+        json.BeginObject(true);+        alloc->PrintParameters(json);+        json.EndObject();+    }+    json.EndArray();+}+#endif // VMA_STATS_STRING_ENABLED++bool VmaDedicatedAllocationList::IsEmpty()+{+    VmaMutexLockRead lock(m_Mutex, m_UseMutex);+    return m_AllocationList.IsEmpty();+}++void VmaDedicatedAllocationList::Register(VmaAllocation alloc)+{+    VmaMutexLockWrite lock(m_Mutex, m_UseMutex);+    m_AllocationList.PushBack(alloc);+}++void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc)+{+    VmaMutexLockWrite lock(m_Mutex, m_UseMutex);+    m_AllocationList.Remove(alloc);+}+#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS+#endif // _VMA_DEDICATED_ALLOCATION_LIST++#ifndef _VMA_SUBALLOCATION+/*+Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as+allocated memory block or free.+*/+struct VmaSuballocation+{+    VkDeviceSize offset;+    VkDeviceSize size;+    void* userData;+    VmaSuballocationType type;+};++// Comparator for offsets.+struct VmaSuballocationOffsetLess+{+    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const+    {+        return lhs.offset < rhs.offset;+    }+};++struct VmaSuballocationOffsetGreater+{+    bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const+    {+        return lhs.offset > rhs.offset;+    }+};++struct VmaSuballocationItemSizeLess+{+    bool operator()(const VmaSuballocationList::iterator lhs,+        const VmaSuballocationList::iterator rhs) const+    {+        return lhs->size < rhs->size;+    }++    bool operator()(const VmaSuballocationList::iterator lhs,+        VkDeviceSize rhsSize) const+    {+        return lhs->size < rhsSize;+    }+};+#endif // _VMA_SUBALLOCATION++#ifndef _VMA_ALLOCATION_REQUEST+/*+Parameters of planned allocation inside a VmaDeviceMemoryBlock.+item points to a FREE suballocation.+*/+struct VmaAllocationRequest+{+    VmaAllocHandle allocHandle;+    VkDeviceSize size;+    VmaSuballocationList::iterator item;+    void* customData;+    uint64_t algorithmData;+    VmaAllocationRequestType type;+};+#endif // _VMA_ALLOCATION_REQUEST++#ifndef _VMA_BLOCK_METADATA+/*+Data structure used for bookkeeping of allocations and unused ranges of memory+in a single VkDeviceMemory block.+*/+class VmaBlockMetadata+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata)+public:+    // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object.+    VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,+        VkDeviceSize bufferImageGranularity, bool isVirtual);+    virtual ~VmaBlockMetadata() = default;++    virtual void Init(VkDeviceSize size) { m_Size = size; }+    bool IsVirtual() const { return m_IsVirtual; }+    VkDeviceSize GetSize() const { return m_Size; }++    // Validates all data structures inside this object. If not valid, returns false.+    virtual bool Validate() const = 0;+    virtual size_t GetAllocationCount() const = 0;+    virtual size_t GetFreeRegionsCount() const = 0;+    virtual VkDeviceSize GetSumFreeSize() const = 0;+    // Returns true if this block is empty - contains only single free suballocation.+    virtual bool IsEmpty() const = 0;+    virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0;+    virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0;+    virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0;++    virtual VmaAllocHandle GetAllocationListBegin() const = 0;+    virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0;+    virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0;++    // Shouldn't modify blockCount.+    virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0;+    virtual void AddStatistics(VmaStatistics& inoutStats) const = 0;++#if VMA_STATS_STRING_ENABLED+    virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0;+#endif++    // Tries to find a place for suballocation with given parameters inside this block.+    // If succeeded, fills pAllocationRequest and returns true.+    // If failed, returns false.+    virtual bool CreateAllocationRequest(+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        bool upperAddress,+        VmaSuballocationType allocType,+        // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags.+        uint32_t strategy,+        VmaAllocationRequest* pAllocationRequest) = 0;++    virtual VkResult CheckCorruption(const void* pBlockData) = 0;++    // Makes actual allocation based on request. Request must already be checked and valid.+    virtual void Alloc(+        const VmaAllocationRequest& request,+        VmaSuballocationType type,+        void* userData) = 0;++    // Frees suballocation assigned to given memory region.+    virtual void Free(VmaAllocHandle allocHandle) = 0;++    // Frees all allocations.+    // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations!+    virtual void Clear() = 0;++    virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0;+    virtual void DebugLogAllAllocations() const = 0;++protected:+    const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; }+    VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }+    VkDeviceSize GetDebugMargin() const { return VkDeviceSize(IsVirtual() ? 0 : VMA_DEBUG_MARGIN); }++    void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const;+#if VMA_STATS_STRING_ENABLED+    // mapRefCount == UINT32_MAX means unspecified.+    void PrintDetailedMap_Begin(class VmaJsonWriter& json,+        VkDeviceSize unusedBytes,+        size_t allocationCount,+        size_t unusedRangeCount) const;+    void PrintDetailedMap_Allocation(class VmaJsonWriter& json,+        VkDeviceSize offset, VkDeviceSize size, void* userData) const;+    static void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,+        VkDeviceSize offset,+        VkDeviceSize size);+    static void PrintDetailedMap_End(class VmaJsonWriter& json);+#endif++private:+    VkDeviceSize m_Size;+    const VkAllocationCallbacks* m_pAllocationCallbacks;+    const VkDeviceSize m_BufferImageGranularity;+    const bool m_IsVirtual;+};++#ifndef _VMA_BLOCK_METADATA_FUNCTIONS+VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks,+    VkDeviceSize bufferImageGranularity, bool isVirtual)+    : m_Size(0),+    m_pAllocationCallbacks(pAllocationCallbacks),+    m_BufferImageGranularity(bufferImageGranularity),+    m_IsVirtual(isVirtual) {}++void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const+{+    if (IsVirtual())+    {+        VMA_LEAK_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p", offset, size, userData);+    }+    else+    {+        VMA_ASSERT(userData != VMA_NULL);+        VmaAllocation allocation = reinterpret_cast<VmaAllocation>(userData);++        userData = allocation->GetUserData();+        const char* name = allocation->GetName();++#if VMA_STATS_STRING_ENABLED+        VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %s; Usage: %" PRIu64,+            offset, size, userData, name ? name : "vma_empty",+            VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()],+            (uint64_t)allocation->GetBufferImageUsage().Value);+#else+        VMA_LEAK_LOG_FORMAT("UNFREED ALLOCATION; Offset: %" PRIu64 "; Size: %" PRIu64 "; UserData: %p; Name: %s; Type: %u",+            offset, size, userData, name ? name : "vma_empty",+            (unsigned)allocation->GetSuballocationType());+#endif // VMA_STATS_STRING_ENABLED+    }++}++#if VMA_STATS_STRING_ENABLED+void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json,+    VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const+{+    json.WriteString("TotalBytes");+    json.WriteNumber(GetSize());++    json.WriteString("UnusedBytes");+    json.WriteNumber(unusedBytes);++    json.WriteString("Allocations");+    json.WriteNumber((uint64_t)allocationCount);++    json.WriteString("UnusedRanges");+    json.WriteNumber((uint64_t)unusedRangeCount);++    json.WriteString("Suballocations");+    json.BeginArray();+}++void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json,+    VkDeviceSize offset, VkDeviceSize size, void* userData) const+{+    json.BeginObject(true);++    json.WriteString("Offset");+    json.WriteNumber(offset);++    if (IsVirtual())+    {+        json.WriteString("Size");+        json.WriteNumber(size);+        if (userData)+        {+            json.WriteString("CustomData");+            json.BeginString();+            json.ContinueString_Pointer(userData);+            json.EndString();+        }+    }+    else+    {+        ((VmaAllocation)userData)->PrintParameters(json);+    }++    json.EndObject();+}++void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json,+    VkDeviceSize offset, VkDeviceSize size)+{+    json.BeginObject(true);++    json.WriteString("Offset");+    json.WriteNumber(offset);++    json.WriteString("Type");+    json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]);++    json.WriteString("Size");+    json.WriteNumber(size);++    json.EndObject();+}++void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json)+{+    json.EndArray();+}+#endif // VMA_STATS_STRING_ENABLED+#endif // _VMA_BLOCK_METADATA_FUNCTIONS+#endif // _VMA_BLOCK_METADATA++#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY+// Before deleting object of this class remember to call 'Destroy()'+class VmaBlockBufferImageGranularity final+{+public:+    struct ValidationContext+    {+        const VkAllocationCallbacks* allocCallbacks;+        uint16_t* pageAllocs;+    };++    explicit VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity);+    ~VmaBlockBufferImageGranularity();++    bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; }++    void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size);+    // Before destroying object you must call free it's memory+    void Destroy(const VkAllocationCallbacks* pAllocationCallbacks);++    void RoundupAllocRequest(VmaSuballocationType allocType,+        VkDeviceSize& inOutAllocSize,+        VkDeviceSize& inOutAllocAlignment) const;++    bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,+        VkDeviceSize allocSize,+        VkDeviceSize blockOffset,+        VkDeviceSize blockSize,+        VmaSuballocationType allocType) const;++    void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size);+    void FreePages(VkDeviceSize offset, VkDeviceSize size);+    void Clear();++    ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks,+        bool isVirutal) const;+    bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const;+    bool FinishValidation(ValidationContext& ctx) const;++private:+    static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256;++    struct RegionInfo+    {+        uint8_t allocType;+        uint16_t allocCount;+    };++    VkDeviceSize m_BufferImageGranularity;+    uint32_t m_RegionCount;+    RegionInfo* m_RegionInfo;++    uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); }+    uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); }++    uint32_t OffsetToPageIndex(VkDeviceSize offset) const;+    static void AllocPage(RegionInfo& page, uint8_t allocType);+};++#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS+VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity)+    : m_BufferImageGranularity(bufferImageGranularity),+    m_RegionCount(0),+    m_RegionInfo(VMA_NULL) {}++VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity()+{+    VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!");+}++void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size)+{+    if (IsEnabled())+    {+        m_RegionCount = static_cast<uint32_t>(VmaDivideRoundingUp(size, m_BufferImageGranularity));+        m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount);+        memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));+    }+}++void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks)+{+    if (m_RegionInfo)+    {+        vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount);+        m_RegionInfo = VMA_NULL;+    }+}++void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType,+    VkDeviceSize& inOutAllocSize,+    VkDeviceSize& inOutAllocAlignment) const+{+    if (m_BufferImageGranularity > 1 &&+        m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY)+    {+        if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN ||+            allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||+            allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)+        {+            inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity);+            inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity);+        }+    }+}++bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset,+    VkDeviceSize allocSize,+    VkDeviceSize blockOffset,+    VkDeviceSize blockSize,+    VmaSuballocationType allocType) const+{+    if (IsEnabled())+    {+        uint32_t startPage = GetStartPage(inOutAllocOffset);+        if (m_RegionInfo[startPage].allocCount > 0 &&+            VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[startPage].allocType), allocType))+        {+            inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity);+            if (blockSize < allocSize + inOutAllocOffset - blockOffset)+                return true;+            ++startPage;+        }+        uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize);+        if (endPage != startPage &&+            m_RegionInfo[endPage].allocCount > 0 &&+            VmaIsBufferImageGranularityConflict(static_cast<VmaSuballocationType>(m_RegionInfo[endPage].allocType), allocType))+        {+            return true;+        }+    }+    return false;+}++void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size)+{+    if (IsEnabled())+    {+        uint32_t startPage = GetStartPage(offset);+        AllocPage(m_RegionInfo[startPage], allocType);++        uint32_t endPage = GetEndPage(offset, size);+        if (startPage != endPage)+            AllocPage(m_RegionInfo[endPage], allocType);+    }+}++void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size)+{+    if (IsEnabled())+    {+        uint32_t startPage = GetStartPage(offset);+        --m_RegionInfo[startPage].allocCount;+        if (m_RegionInfo[startPage].allocCount == 0)+            m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;+        uint32_t endPage = GetEndPage(offset, size);+        if (startPage != endPage)+        {+            --m_RegionInfo[endPage].allocCount;+            if (m_RegionInfo[endPage].allocCount == 0)+                m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE;+        }+    }+}++void VmaBlockBufferImageGranularity::Clear()+{+    if (m_RegionInfo)+        memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo));+}++VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation(+    const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const+{+    ValidationContext ctx{ pAllocationCallbacks, VMA_NULL };+    if (!isVirutal && IsEnabled())+    {+        ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount);+        memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t));+    }+    return ctx;+}++bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx,+    VkDeviceSize offset, VkDeviceSize size) const+{+    if (IsEnabled())+    {+        uint32_t start = GetStartPage(offset);+        ++ctx.pageAllocs[start];+        VMA_VALIDATE(m_RegionInfo[start].allocCount > 0);++        uint32_t end = GetEndPage(offset, size);+        if (start != end)+        {+            ++ctx.pageAllocs[end];+            VMA_VALIDATE(m_RegionInfo[end].allocCount > 0);+        }+    }+    return true;+}++bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const+{+    // Check proper page structure+    if (IsEnabled())+    {+        VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!");++        for (uint32_t page = 0; page < m_RegionCount; ++page)+        {+            VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount);+        }+        vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount);+        ctx.pageAllocs = VMA_NULL;+    }+    return true;+}++uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const+{+    return static_cast<uint32_t>(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity));+}++void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType)+{+    // When current alloc type is free then it can be overridden by new type+    if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE))+        page.allocType = allocType;++    ++page.allocCount;+}+#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS+#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY++#ifndef _VMA_BLOCK_METADATA_LINEAR+/*+Allocations and their references in internal data structure look like this:++if(m_2ndVectorMode == SECOND_VECTOR_EMPTY):++        0 +-------++          |       |+          |       |+          |       |+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount]+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount + 1]+          +-------++          |  ...  |+          +-------++          | Alloc |  1st[1st.size() - 1]+          +-------++          |       |+          |       |+          |       |+GetSize() +-------+++if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER):++        0 +-------++          | Alloc |  2nd[0]+          +-------++          | Alloc |  2nd[1]+          +-------++          |  ...  |+          +-------++          | Alloc |  2nd[2nd.size() - 1]+          +-------++          |       |+          |       |+          |       |+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount]+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount + 1]+          +-------++          |  ...  |+          +-------++          | Alloc |  1st[1st.size() - 1]+          +-------++          |       |+GetSize() +-------+++if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK):++        0 +-------++          |       |+          |       |+          |       |+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount]+          +-------++          | Alloc |  1st[m_1stNullItemsBeginCount + 1]+          +-------++          |  ...  |+          +-------++          | Alloc |  1st[1st.size() - 1]+          +-------++          |       |+          |       |+          |       |+          +-------++          | Alloc |  2nd[2nd.size() - 1]+          +-------++          |  ...  |+          +-------++          | Alloc |  2nd[1]+          +-------++          | Alloc |  2nd[0]+GetSize() +-------+++*/+class VmaBlockMetadata_Linear : public VmaBlockMetadata+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_Linear)+public:+    VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,+        VkDeviceSize bufferImageGranularity, bool isVirtual);+    ~VmaBlockMetadata_Linear() override = default;++    VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; }+    bool IsEmpty() const override { return GetAllocationCount() == 0; }+    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; }++    void Init(VkDeviceSize size) override;+    bool Validate() const override;+    size_t GetAllocationCount() const override;+    size_t GetFreeRegionsCount() const override;++    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;+    void AddStatistics(VmaStatistics& inoutStats) const override;++#if VMA_STATS_STRING_ENABLED+    void PrintDetailedMap(class VmaJsonWriter& json) const override;+#endif++    bool CreateAllocationRequest(+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        bool upperAddress,+        VmaSuballocationType allocType,+        uint32_t strategy,+        VmaAllocationRequest* pAllocationRequest) override;++    VkResult CheckCorruption(const void* pBlockData) override;++    void Alloc(+        const VmaAllocationRequest& request,+        VmaSuballocationType type,+        void* userData) override;++    void Free(VmaAllocHandle allocHandle) override;+    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;+    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;+    VmaAllocHandle GetAllocationListBegin() const override;+    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;+    VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;+    void Clear() override;+    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;+    void DebugLogAllAllocations() const override;++private:+    /*+    There are two suballocation vectors, used in ping-pong way.+    The one with index m_1stVectorIndex is called 1st.+    The one with index (m_1stVectorIndex ^ 1) is called 2nd.+    2nd can be non-empty only when 1st is not empty.+    When 2nd is not empty, m_2ndVectorMode indicates its mode of operation.+    */+    typedef VmaVector<VmaSuballocation, VmaStlAllocator<VmaSuballocation>> SuballocationVectorType;++    enum SECOND_VECTOR_MODE+    {+        SECOND_VECTOR_EMPTY,+        /*+        Suballocations in 2nd vector are created later than the ones in 1st, but they+        all have smaller offset.+        */+        SECOND_VECTOR_RING_BUFFER,+        /*+        Suballocations in 2nd vector are upper side of double stack.+        They all have offsets higher than those in 1st vector.+        Top of this stack means smaller offsets, but higher indices in this vector.+        */+        SECOND_VECTOR_DOUBLE_STACK,+    };++    VkDeviceSize m_SumFreeSize;+    SuballocationVectorType m_Suballocations0, m_Suballocations1;+    uint32_t m_1stVectorIndex;+    SECOND_VECTOR_MODE m_2ndVectorMode;+    // Number of items in 1st vector with hAllocation = null at the beginning.+    size_t m_1stNullItemsBeginCount;+    // Number of other items in 1st vector with hAllocation = null somewhere in the middle.+    size_t m_1stNullItemsMiddleCount;+    // Number of items in 2nd vector with hAllocation = null.+    size_t m_2ndNullItemsCount;++    SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }+    SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }+    const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; }+    const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; }++    VmaSuballocation& FindSuballocation(VkDeviceSize offset) const;+    bool ShouldCompact1st() const;+    void CleanupAfterFree();++    bool CreateAllocationRequest_LowerAddress(+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        VmaSuballocationType allocType,+        uint32_t strategy,+        VmaAllocationRequest* pAllocationRequest);+    bool CreateAllocationRequest_UpperAddress(+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        VmaSuballocationType allocType,+        uint32_t strategy,+        VmaAllocationRequest* pAllocationRequest);+};++#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS+VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks,+    VkDeviceSize bufferImageGranularity, bool isVirtual)+    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),+    m_SumFreeSize(0),+    m_Suballocations0(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),+    m_Suballocations1(VmaStlAllocator<VmaSuballocation>(pAllocationCallbacks)),+    m_1stVectorIndex(0),+    m_2ndVectorMode(SECOND_VECTOR_EMPTY),+    m_1stNullItemsBeginCount(0),+    m_1stNullItemsMiddleCount(0),+    m_2ndNullItemsCount(0) {}++void VmaBlockMetadata_Linear::Init(VkDeviceSize size)+{+    VmaBlockMetadata::Init(size);+    m_SumFreeSize = size;+}++bool VmaBlockMetadata_Linear::Validate() const+{+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++    VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY));+    VMA_VALIDATE(!suballocations1st.empty() ||+        suballocations2nd.empty() ||+        m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER);++    if (!suballocations1st.empty())+    {+        // Null item at the beginning should be accounted into m_1stNullItemsBeginCount.+        VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE);+        // Null item at the end should be just pop_back().+        VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE);+    }+    if (!suballocations2nd.empty())+    {+        // Null item at the end should be just pop_back().+        VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE);+    }++    VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size());+    VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size());++    VkDeviceSize sumUsedSize = 0;+    const size_t suballoc1stCount = suballocations1st.size();+    const VkDeviceSize debugMargin = GetDebugMargin();+    VkDeviceSize offset = 0;++    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        const size_t suballoc2ndCount = suballocations2nd.size();+        size_t nullItem2ndCount = 0;+        for (size_t i = 0; i < suballoc2ndCount; ++i)+        {+            const VmaSuballocation& suballoc = suballocations2nd[i];+            const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);++            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;+            if (!IsVirtual())+            {+                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));+            }+            VMA_VALIDATE(suballoc.offset >= offset);++            if (!currFree)+            {+                if (!IsVirtual())+                {+                    VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);+                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);+                }+                sumUsedSize += suballoc.size;+            }+            else+            {+                ++nullItem2ndCount;+            }++            offset = suballoc.offset + suballoc.size + debugMargin;+        }++        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);+    }++    for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i)+    {+        const VmaSuballocation& suballoc = suballocations1st[i];+        VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE &&+            suballoc.userData == VMA_NULL);+    }++    size_t nullItem1stCount = m_1stNullItemsBeginCount;++    for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i)+    {+        const VmaSuballocation& suballoc = suballocations1st[i];+        const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);++        VmaAllocation const alloc = (VmaAllocation)suballoc.userData;+        if (!IsVirtual())+        {+            VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));+        }+        VMA_VALIDATE(suballoc.offset >= offset);+        VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree);++        if (!currFree)+        {+            if (!IsVirtual())+            {+                VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);+                VMA_VALIDATE(alloc->GetSize() == suballoc.size);+            }+            sumUsedSize += suballoc.size;+        }+        else+        {+            ++nullItem1stCount;+        }++        offset = suballoc.offset + suballoc.size + debugMargin;+    }+    VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount);++    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        const size_t suballoc2ndCount = suballocations2nd.size();+        size_t nullItem2ndCount = 0;+        for (size_t i = suballoc2ndCount; i--; )+        {+            const VmaSuballocation& suballoc = suballocations2nd[i];+            const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);++            VmaAllocation const alloc = (VmaAllocation)suballoc.userData;+            if (!IsVirtual())+            {+                VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE));+            }+            VMA_VALIDATE(suballoc.offset >= offset);++            if (!currFree)+            {+                if (!IsVirtual())+                {+                    VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1);+                    VMA_VALIDATE(alloc->GetSize() == suballoc.size);+                }+                sumUsedSize += suballoc.size;+            }+            else+            {+                ++nullItem2ndCount;+            }++            offset = suballoc.offset + suballoc.size + debugMargin;+        }++        VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount);+    }++    VMA_VALIDATE(offset <= GetSize());+    VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize);++    return true;+}++size_t VmaBlockMetadata_Linear::GetAllocationCount() const+{+    return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount ++        AccessSuballocations2nd().size() - m_2ndNullItemsCount;+}++size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const+{+    // Function only used for defragmentation, which is disabled for this algorithm+    VMA_ASSERT(0);+    return SIZE_MAX;+}++void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const+{+    const VkDeviceSize size = GetSize();+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    const size_t suballoc1stCount = suballocations1st.size();+    const size_t suballoc2ndCount = suballocations2nd.size();++    inoutStats.statistics.blockCount++;+    inoutStats.statistics.blockBytes += size;++    VkDeviceSize lastOffset = 0;++    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;+        size_t nextAlloc2ndIndex = 0;+        while (lastOffset < freeSpace2ndTo1stEnd)+        {+            // Find next non-null allocation or move nextAllocIndex to the end.+            while (nextAlloc2ndIndex < suballoc2ndCount &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                ++nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex < suballoc2ndCount)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                ++nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                // There is free space from lastOffset to freeSpace2ndTo1stEnd.+                if (lastOffset < freeSpace2ndTo1stEnd)+                {+                    const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;+                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+                }++                // End of loop.+                lastOffset = freeSpace2ndTo1stEnd;+            }+        }+    }++    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;+    const VkDeviceSize freeSpace1stTo2ndEnd =+        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;+    while (lastOffset < freeSpace1stTo2ndEnd)+    {+        // Find next non-null allocation or move nextAllocIndex to the end.+        while (nextAlloc1stIndex < suballoc1stCount &&+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)+        {+            ++nextAlloc1stIndex;+        }++        // Found non-null allocation.+        if (nextAlloc1stIndex < suballoc1stCount)+        {+            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];++            // 1. Process free space before this allocation.+            if (lastOffset < suballoc.offset)+            {+                // There is free space from lastOffset to suballoc.offset.+                const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+            }++            // 2. Process this allocation.+            // There is allocation with suballoc.offset, suballoc.size.+            VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);++            // 3. Prepare for next iteration.+            lastOffset = suballoc.offset + suballoc.size;+            ++nextAlloc1stIndex;+        }+        // We are at the end.+        else+        {+            // There is free space from lastOffset to freeSpace1stTo2ndEnd.+            if (lastOffset < freeSpace1stTo2ndEnd)+            {+                const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;+                VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+            }++            // End of loop.+            lastOffset = freeSpace1stTo2ndEnd;+        }+    }++    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;+        while (lastOffset < size)+        {+            // Find next non-null allocation or move nextAllocIndex to the end.+            while (nextAlloc2ndIndex != SIZE_MAX &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                --nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex != SIZE_MAX)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size);++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                --nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                // There is free space from lastOffset to size.+                if (lastOffset < size)+                {+                    const VkDeviceSize unusedRangeSize = size - lastOffset;+                    VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize);+                }++                // End of loop.+                lastOffset = size;+            }+        }+    }+}++void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const+{+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    const VkDeviceSize size = GetSize();+    const size_t suballoc1stCount = suballocations1st.size();+    const size_t suballoc2ndCount = suballocations2nd.size();++    inoutStats.blockCount++;+    inoutStats.blockBytes += size;+    inoutStats.allocationBytes += size - m_SumFreeSize;++    VkDeviceSize lastOffset = 0;++    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;+        size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount;+        while (lastOffset < freeSpace2ndTo1stEnd)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex < suballoc2ndCount &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                ++nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex < suballoc2ndCount)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                ++inoutStats.allocationCount;++                // Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                ++nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                // End of loop.+                lastOffset = freeSpace2ndTo1stEnd;+            }+        }+    }++    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;+    const VkDeviceSize freeSpace1stTo2ndEnd =+        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;+    while (lastOffset < freeSpace1stTo2ndEnd)+    {+        // Find next non-null allocation or move nextAllocIndex to the end.+        while (nextAlloc1stIndex < suballoc1stCount &&+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)+        {+            ++nextAlloc1stIndex;+        }++        // Found non-null allocation.+        if (nextAlloc1stIndex < suballoc1stCount)+        {+            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];++            // Process this allocation.+            // There is allocation with suballoc.offset, suballoc.size.+            ++inoutStats.allocationCount;++            // Prepare for next iteration.+            lastOffset = suballoc.offset + suballoc.size;+            ++nextAlloc1stIndex;+        }+        // We are at the end.+        else+        {+            // End of loop.+            lastOffset = freeSpace1stTo2ndEnd;+        }+    }++    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;+        while (lastOffset < size)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex != SIZE_MAX &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                --nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex != SIZE_MAX)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                ++inoutStats.allocationCount;++                // Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                --nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                // End of loop.+                lastOffset = size;+            }+        }+    }+}++#if VMA_STATS_STRING_ENABLED+void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const+{+    const VkDeviceSize size = GetSize();+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    const size_t suballoc1stCount = suballocations1st.size();+    const size_t suballoc2ndCount = suballocations2nd.size();++    // FIRST PASS++    size_t unusedRangeCount = 0;+    VkDeviceSize usedBytes = 0;++    VkDeviceSize lastOffset = 0;++    size_t alloc2ndCount = 0;+    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;+        size_t nextAlloc2ndIndex = 0;+        while (lastOffset < freeSpace2ndTo1stEnd)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex < suballoc2ndCount &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                ++nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex < suballoc2ndCount)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    ++unusedRangeCount;+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                ++alloc2ndCount;+                usedBytes += suballoc.size;++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                ++nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                if (lastOffset < freeSpace2ndTo1stEnd)+                {+                    // There is free space from lastOffset to freeSpace2ndTo1stEnd.+                    ++unusedRangeCount;+                }++                // End of loop.+                lastOffset = freeSpace2ndTo1stEnd;+            }+        }+    }++    size_t nextAlloc1stIndex = m_1stNullItemsBeginCount;+    size_t alloc1stCount = 0;+    const VkDeviceSize freeSpace1stTo2ndEnd =+        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size;+    while (lastOffset < freeSpace1stTo2ndEnd)+    {+        // Find next non-null allocation or move nextAllocIndex to the end.+        while (nextAlloc1stIndex < suballoc1stCount &&+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)+        {+            ++nextAlloc1stIndex;+        }++        // Found non-null allocation.+        if (nextAlloc1stIndex < suballoc1stCount)+        {+            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];++            // 1. Process free space before this allocation.+            if (lastOffset < suballoc.offset)+            {+                // There is free space from lastOffset to suballoc.offset.+                ++unusedRangeCount;+            }++            // 2. Process this allocation.+            // There is allocation with suballoc.offset, suballoc.size.+            ++alloc1stCount;+            usedBytes += suballoc.size;++            // 3. Prepare for next iteration.+            lastOffset = suballoc.offset + suballoc.size;+            ++nextAlloc1stIndex;+        }+        // We are at the end.+        else+        {+            if (lastOffset < freeSpace1stTo2ndEnd)+            {+                // There is free space from lastOffset to freeSpace1stTo2ndEnd.+                ++unusedRangeCount;+            }++            // End of loop.+            lastOffset = freeSpace1stTo2ndEnd;+        }+    }++    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;+        while (lastOffset < size)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex != SIZE_MAX &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                --nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex != SIZE_MAX)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    ++unusedRangeCount;+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                ++alloc2ndCount;+                usedBytes += suballoc.size;++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                --nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                if (lastOffset < size)+                {+                    // There is free space from lastOffset to size.+                    ++unusedRangeCount;+                }++                // End of loop.+                lastOffset = size;+            }+        }+    }++    const VkDeviceSize unusedBytes = size - usedBytes;+    PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount);++    // SECOND PASS+    lastOffset = 0;++    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset;+        size_t nextAlloc2ndIndex = 0;+        while (lastOffset < freeSpace2ndTo1stEnd)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex < suballoc2ndCount &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                ++nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex < suballoc2ndCount)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                ++nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                if (lastOffset < freeSpace2ndTo1stEnd)+                {+                    // There is free space from lastOffset to freeSpace2ndTo1stEnd.+                    const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset;+                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+                }++                // End of loop.+                lastOffset = freeSpace2ndTo1stEnd;+            }+        }+    }++    nextAlloc1stIndex = m_1stNullItemsBeginCount;+    while (lastOffset < freeSpace1stTo2ndEnd)+    {+        // Find next non-null allocation or move nextAllocIndex to the end.+        while (nextAlloc1stIndex < suballoc1stCount &&+            suballocations1st[nextAlloc1stIndex].userData == VMA_NULL)+        {+            ++nextAlloc1stIndex;+        }++        // Found non-null allocation.+        if (nextAlloc1stIndex < suballoc1stCount)+        {+            const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex];++            // 1. Process free space before this allocation.+            if (lastOffset < suballoc.offset)+            {+                // There is free space from lastOffset to suballoc.offset.+                const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+            }++            // 2. Process this allocation.+            // There is allocation with suballoc.offset, suballoc.size.+            PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);++            // 3. Prepare for next iteration.+            lastOffset = suballoc.offset + suballoc.size;+            ++nextAlloc1stIndex;+        }+        // We are at the end.+        else+        {+            if (lastOffset < freeSpace1stTo2ndEnd)+            {+                // There is free space from lastOffset to freeSpace1stTo2ndEnd.+                const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset;+                PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+            }++            // End of loop.+            lastOffset = freeSpace1stTo2ndEnd;+        }+    }++    if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        size_t nextAlloc2ndIndex = suballocations2nd.size() - 1;+        while (lastOffset < size)+        {+            // Find next non-null allocation or move nextAlloc2ndIndex to the end.+            while (nextAlloc2ndIndex != SIZE_MAX &&+                suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL)+            {+                --nextAlloc2ndIndex;+            }++            // Found non-null allocation.+            if (nextAlloc2ndIndex != SIZE_MAX)+            {+                const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex];++                // 1. Process free space before this allocation.+                if (lastOffset < suballoc.offset)+                {+                    // There is free space from lastOffset to suballoc.offset.+                    const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset;+                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+                }++                // 2. Process this allocation.+                // There is allocation with suballoc.offset, suballoc.size.+                PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData);++                // 3. Prepare for next iteration.+                lastOffset = suballoc.offset + suballoc.size;+                --nextAlloc2ndIndex;+            }+            // We are at the end.+            else+            {+                if (lastOffset < size)+                {+                    // There is free space from lastOffset to size.+                    const VkDeviceSize unusedRangeSize = size - lastOffset;+                    PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize);+                }++                // End of loop.+                lastOffset = size;+            }+        }+    }++    PrintDetailedMap_End(json);+}+#endif // VMA_STATS_STRING_ENABLED++bool VmaBlockMetadata_Linear::CreateAllocationRequest(+    VkDeviceSize allocSize,+    VkDeviceSize allocAlignment,+    bool upperAddress,+    VmaSuballocationType allocType,+    uint32_t strategy,+    VmaAllocationRequest* pAllocationRequest)+{+    VMA_ASSERT(allocSize > 0);+    VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);+    VMA_ASSERT(pAllocationRequest != VMA_NULL);+    VMA_HEAVY_ASSERT(Validate());++    if(allocSize > GetSize())+        return false;++    pAllocationRequest->size = allocSize;+    return upperAddress ?+        CreateAllocationRequest_UpperAddress(+            allocSize, allocAlignment, allocType, strategy, pAllocationRequest) :+        CreateAllocationRequest_LowerAddress(+            allocSize, allocAlignment, allocType, strategy, pAllocationRequest);+}++VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData)+{+    VMA_ASSERT(!IsVirtual());+    SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i)+    {+        const VmaSuballocation& suballoc = suballocations1st[i];+        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)+        {+            if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))+            {+                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");+                return VK_ERROR_UNKNOWN_COPY;+            }+        }+    }++    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i)+    {+        const VmaSuballocation& suballoc = suballocations2nd[i];+        if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)+        {+            if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size))+            {+                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");+                return VK_ERROR_UNKNOWN_COPY;+            }+        }+    }++    return VK_SUCCESS;+}++void VmaBlockMetadata_Linear::Alloc(+    const VmaAllocationRequest& request,+    VmaSuballocationType type,+    void* userData)+{+    const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1;+    const VmaSuballocation newSuballoc = { offset, request.size, userData, type };++    switch (request.type)+    {+    case VmaAllocationRequestType::UpperAddress:+    {+        VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER &&+            "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer.");+        SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+        suballocations2nd.push_back(newSuballoc);+        m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK;+    }+    break;+    case VmaAllocationRequestType::EndOf1st:+    {+        SuballocationVectorType& suballocations1st = AccessSuballocations1st();++        VMA_ASSERT(suballocations1st.empty() ||+            offset >= suballocations1st.back().offset + suballocations1st.back().size);+        // Check if it fits before the end of the block.+        VMA_ASSERT(offset + request.size <= GetSize());++        suballocations1st.push_back(newSuballoc);+    }+    break;+    case VmaAllocationRequestType::EndOf2nd:+    {+        SuballocationVectorType& suballocations1st = AccessSuballocations1st();+        // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector.+        VMA_ASSERT(!suballocations1st.empty() &&+            offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset);+        SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++        switch (m_2ndVectorMode)+        {+        case SECOND_VECTOR_EMPTY:+            // First allocation from second part ring buffer.+            VMA_ASSERT(suballocations2nd.empty());+            m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER;+            break;+        case SECOND_VECTOR_RING_BUFFER:+            // 2-part ring buffer is already started.+            VMA_ASSERT(!suballocations2nd.empty());+            break;+        case SECOND_VECTOR_DOUBLE_STACK:+            VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack.");+            break;+        default:+            VMA_ASSERT(0);+        }++        suballocations2nd.push_back(newSuballoc);+    }+    break;+    default:+        VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR.");+    }++    m_SumFreeSize -= newSuballoc.size;+}++void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle)+{+    SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    VkDeviceSize offset = (VkDeviceSize)allocHandle - 1;++    if (!suballocations1st.empty())+    {+        // First allocation: Mark it as next empty at the beginning.+        VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount];+        if (firstSuballoc.offset == offset)+        {+            firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;+            firstSuballoc.userData = VMA_NULL;+            m_SumFreeSize += firstSuballoc.size;+            ++m_1stNullItemsBeginCount;+            CleanupAfterFree();+            return;+        }+    }++    // Last allocation in 2-part ring buffer or top of upper stack (same logic).+    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ||+        m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        VmaSuballocation& lastSuballoc = suballocations2nd.back();+        if (lastSuballoc.offset == offset)+        {+            m_SumFreeSize += lastSuballoc.size;+            suballocations2nd.pop_back();+            CleanupAfterFree();+            return;+        }+    }+    // Last allocation in 1st vector.+    else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY)+    {+        VmaSuballocation& lastSuballoc = suballocations1st.back();+        if (lastSuballoc.offset == offset)+        {+            m_SumFreeSize += lastSuballoc.size;+            suballocations1st.pop_back();+            CleanupAfterFree();+            return;+        }+    }++    VmaSuballocation refSuballoc;+    refSuballoc.offset = offset;+    // Rest of members stays uninitialized intentionally for better performance.++    // Item from the middle of 1st vector.+    {+        const SuballocationVectorType::iterator it = VmaBinaryFindSorted(+            suballocations1st.begin() + m_1stNullItemsBeginCount,+            suballocations1st.end(),+            refSuballoc,+            VmaSuballocationOffsetLess());+        if (it != suballocations1st.end())+        {+            it->type = VMA_SUBALLOCATION_TYPE_FREE;+            it->userData = VMA_NULL;+            ++m_1stNullItemsMiddleCount;+            m_SumFreeSize += it->size;+            CleanupAfterFree();+            return;+        }+    }++    if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)+    {+        // Item from the middle of 2nd vector.+        const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?+            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :+            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());+        if (it != suballocations2nd.end())+        {+            it->type = VMA_SUBALLOCATION_TYPE_FREE;+            it->userData = VMA_NULL;+            ++m_2ndNullItemsCount;+            m_SumFreeSize += it->size;+            CleanupAfterFree();+            return;+        }+    }++    VMA_ASSERT(0 && "Allocation to free not found in linear allocator!");+}++void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)+{+    outInfo.offset = (VkDeviceSize)allocHandle - 1;+    VmaSuballocation& suballoc = FindSuballocation(outInfo.offset);+    outInfo.size = suballoc.size;+    outInfo.pUserData = suballoc.userData;+}++void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const+{+    return FindSuballocation((VkDeviceSize)allocHandle - 1).userData;+}++VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const+{+    // Function only used for defragmentation, which is disabled for this algorithm+    VMA_ASSERT(0);+    return VK_NULL_HANDLE;+}++VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const+{+    // Function only used for defragmentation, which is disabled for this algorithm+    VMA_ASSERT(0);+    return VK_NULL_HANDLE;+}++VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const+{+    // Function only used for defragmentation, which is disabled for this algorithm+    VMA_ASSERT(0);+    return 0;+}++void VmaBlockMetadata_Linear::Clear()+{+    m_SumFreeSize = GetSize();+    m_Suballocations0.clear();+    m_Suballocations1.clear();+    // Leaving m_1stVectorIndex unchanged - it doesn't matter.+    m_2ndVectorMode = SECOND_VECTOR_EMPTY;+    m_1stNullItemsBeginCount = 0;+    m_1stNullItemsMiddleCount = 0;+    m_2ndNullItemsCount = 0;+}++void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)+{+    VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1);+    suballoc.userData = userData;+}++void VmaBlockMetadata_Linear::DebugLogAllAllocations() const+{+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it)+        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)+            DebugLogAllocation(it->offset, it->size, it->userData);++    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();+    for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it)+        if (it->type != VMA_SUBALLOCATION_TYPE_FREE)+            DebugLogAllocation(it->offset, it->size, it->userData);+}++VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const+{+    const SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++    VmaSuballocation refSuballoc;+    refSuballoc.offset = offset;+    // Rest of members stays uninitialized intentionally for better performance.++    // Item from the 1st vector.+    {+        SuballocationVectorType::const_iterator it = VmaBinaryFindSorted(+            suballocations1st.begin() + m_1stNullItemsBeginCount,+            suballocations1st.end(),+            refSuballoc,+            VmaSuballocationOffsetLess());+        if (it != suballocations1st.end())+        {+            return const_cast<VmaSuballocation&>(*it);+        }+    }++    if (m_2ndVectorMode != SECOND_VECTOR_EMPTY)+    {+        // Rest of members stays uninitialized intentionally for better performance.+        SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ?+            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) :+            VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater());+        if (it != suballocations2nd.end())+        {+            return const_cast<VmaSuballocation&>(*it);+        }+    }++    VMA_ASSERT(0 && "Allocation not found in linear allocator!");+    return const_cast<VmaSuballocation&>(suballocations1st.back()); // Should never occur.+}++bool VmaBlockMetadata_Linear::ShouldCompact1st() const+{+    const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;+    const size_t suballocCount = AccessSuballocations1st().size();+    return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3;+}++void VmaBlockMetadata_Linear::CleanupAfterFree()+{+    SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++    if (IsEmpty())+    {+        suballocations1st.clear();+        suballocations2nd.clear();+        m_1stNullItemsBeginCount = 0;+        m_1stNullItemsMiddleCount = 0;+        m_2ndNullItemsCount = 0;+        m_2ndVectorMode = SECOND_VECTOR_EMPTY;+    }+    else+    {+        const size_t suballoc1stCount = suballocations1st.size();+        const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount;+        VMA_ASSERT(nullItem1stCount <= suballoc1stCount);++        // Find more null items at the beginning of 1st vector.+        while (m_1stNullItemsBeginCount < suballoc1stCount &&+            suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)+        {+            ++m_1stNullItemsBeginCount;+            --m_1stNullItemsMiddleCount;+        }++        // Find more null items at the end of 1st vector.+        while (m_1stNullItemsMiddleCount > 0 &&+            suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE)+        {+            --m_1stNullItemsMiddleCount;+            suballocations1st.pop_back();+        }++        // Find more null items at the end of 2nd vector.+        while (m_2ndNullItemsCount > 0 &&+            suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE)+        {+            --m_2ndNullItemsCount;+            suballocations2nd.pop_back();+        }++        // Find more null items at the beginning of 2nd vector.+        while (m_2ndNullItemsCount > 0 &&+            suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE)+        {+            --m_2ndNullItemsCount;+            VmaVectorRemove(suballocations2nd, 0);+        }++        if (ShouldCompact1st())+        {+            const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount;+            size_t srcIndex = m_1stNullItemsBeginCount;+            for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex)+            {+                while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE)+                {+                    ++srcIndex;+                }+                if (dstIndex != srcIndex)+                {+                    suballocations1st[dstIndex] = suballocations1st[srcIndex];+                }+                ++srcIndex;+            }+            suballocations1st.resize(nonNullItemCount);+            m_1stNullItemsBeginCount = 0;+            m_1stNullItemsMiddleCount = 0;+        }++        // 2nd vector became empty.+        if (suballocations2nd.empty())+        {+            m_2ndVectorMode = SECOND_VECTOR_EMPTY;+        }++        // 1st vector became empty.+        if (suballocations1st.size() - m_1stNullItemsBeginCount == 0)+        {+            suballocations1st.clear();+            m_1stNullItemsBeginCount = 0;++            if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+            {+                // Swap 1st with 2nd. Now 2nd is empty.+                m_2ndVectorMode = SECOND_VECTOR_EMPTY;+                m_1stNullItemsMiddleCount = m_2ndNullItemsCount;+                while (m_1stNullItemsBeginCount < suballocations2nd.size() &&+                    suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE)+                {+                    ++m_1stNullItemsBeginCount;+                    --m_1stNullItemsMiddleCount;+                }+                m_2ndNullItemsCount = 0;+                m_1stVectorIndex ^= 1;+            }+        }+    }++    VMA_HEAVY_ASSERT(Validate());+}++bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress(+    VkDeviceSize allocSize,+    VkDeviceSize allocAlignment,+    VmaSuballocationType allocType,+    uint32_t strategy,+    VmaAllocationRequest* pAllocationRequest)+{+    const VkDeviceSize blockSize = GetSize();+    const VkDeviceSize debugMargin = GetDebugMargin();+    const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();+    SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++    if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+    {+        // Try to allocate at the end of 1st vector.++        VkDeviceSize resultBaseOffset = 0;+        if (!suballocations1st.empty())+        {+            const VmaSuballocation& lastSuballoc = suballocations1st.back();+            resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;+        }++        // Start from offset equal to beginning of free space.+        VkDeviceSize resultOffset = resultBaseOffset;++        // Apply alignment.+        resultOffset = VmaAlignUp(resultOffset, allocAlignment);++        // Check previous suballocations for BufferImageGranularity conflicts.+        // Make bigger alignment if necessary.+        if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty())+        {+            bool bufferImageGranularityConflict = false;+            for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )+            {+                const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];+                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))+                {+                    if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))+                    {+                        bufferImageGranularityConflict = true;+                        break;+                    }+                }+                else+                    // Already on previous page.+                    break;+            }+            if (bufferImageGranularityConflict)+            {+                resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);+            }+        }++        const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ?+            suballocations2nd.back().offset : blockSize;++        // There is enough free space at the end after alignment.+        if (resultOffset + allocSize + debugMargin <= freeSpaceEnd)+        {+            // Check next suballocations for BufferImageGranularity conflicts.+            // If conflict exists, allocation cannot be made here.+            if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK)+            {+                for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )+                {+                    const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];+                    if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))+                    {+                        if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))+                        {+                            return false;+                        }+                    }+                    else+                    {+                        // Already on previous page.+                        break;+                    }+                }+            }++            // All tests passed: Success.+            pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);+            // pAllocationRequest->item, customData unused.+            pAllocationRequest->type = VmaAllocationRequestType::EndOf1st;+            return true;+        }+    }++    // Wrap-around to end of 2nd vector. Try to allocate there, watching for the+    // beginning of 1st vector as the end of free space.+    if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        VMA_ASSERT(!suballocations1st.empty());++        VkDeviceSize resultBaseOffset = 0;+        if (!suballocations2nd.empty())+        {+            const VmaSuballocation& lastSuballoc = suballocations2nd.back();+            resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin;+        }++        // Start from offset equal to beginning of free space.+        VkDeviceSize resultOffset = resultBaseOffset;++        // Apply alignment.+        resultOffset = VmaAlignUp(resultOffset, allocAlignment);++        // Check previous suballocations for BufferImageGranularity conflicts.+        // Make bigger alignment if necessary.+        if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())+        {+            bool bufferImageGranularityConflict = false;+            for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; )+            {+                const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex];+                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))+                {+                    if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))+                    {+                        bufferImageGranularityConflict = true;+                        break;+                    }+                }+                else+                    // Already on previous page.+                    break;+            }+            if (bufferImageGranularityConflict)+            {+                resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity);+            }+        }++        size_t index1st = m_1stNullItemsBeginCount;++        // There is enough free space at the end after alignment.+        if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) ||+            (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset))+        {+            // Check next suballocations for BufferImageGranularity conflicts.+            // If conflict exists, allocation cannot be made here.+            if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity)+            {+                for (size_t nextSuballocIndex = index1st;+                    nextSuballocIndex < suballocations1st.size();+                    nextSuballocIndex++)+                {+                    const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex];+                    if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))+                    {+                        if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))+                        {+                            return false;+                        }+                    }+                    else+                    {+                        // Already on next page.+                        break;+                    }+                }+            }++            // All tests passed: Success.+            pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);+            pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd;+            // pAllocationRequest->item, customData unused.+            return true;+        }+    }++    return false;+}++bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress(+    VkDeviceSize allocSize,+    VkDeviceSize allocAlignment,+    VmaSuballocationType allocType,+    uint32_t strategy,+    VmaAllocationRequest* pAllocationRequest)+{+    const VkDeviceSize blockSize = GetSize();+    const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();+    SuballocationVectorType& suballocations1st = AccessSuballocations1st();+    SuballocationVectorType& suballocations2nd = AccessSuballocations2nd();++    if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER)+    {+        VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer.");+        return false;+    }++    // Try to allocate before 2nd.back(), or end of block if 2nd.empty().+    if (allocSize > blockSize)+    {+        return false;+    }+    VkDeviceSize resultBaseOffset = blockSize - allocSize;+    if (!suballocations2nd.empty())+    {+        const VmaSuballocation& lastSuballoc = suballocations2nd.back();+        resultBaseOffset = lastSuballoc.offset - allocSize;+        if (allocSize > lastSuballoc.offset)+        {+            return false;+        }+    }++    // Start from offset equal to end of free space.+    VkDeviceSize resultOffset = resultBaseOffset;++    const VkDeviceSize debugMargin = GetDebugMargin();++    // Apply debugMargin at the end.+    if (debugMargin > 0)+    {+        if (resultOffset < debugMargin)+        {+            return false;+        }+        resultOffset -= debugMargin;+    }++    // Apply alignment.+    resultOffset = VmaAlignDown(resultOffset, allocAlignment);++    // Check next suballocations from 2nd for BufferImageGranularity conflicts.+    // Make bigger alignment if necessary.+    if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty())+    {+        bool bufferImageGranularityConflict = false;+        for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; )+        {+            const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex];+            if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))+            {+                if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType))+                {+                    bufferImageGranularityConflict = true;+                    break;+                }+            }+            else+                // Already on previous page.+                break;+        }+        if (bufferImageGranularityConflict)+        {+            resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity);+        }+    }++    // There is enough free space.+    const VkDeviceSize endOf1st = !suballocations1st.empty() ?+        suballocations1st.back().offset + suballocations1st.back().size :+        0;+    if (endOf1st + debugMargin <= resultOffset)+    {+        // Check previous suballocations for BufferImageGranularity conflicts.+        // If conflict exists, allocation cannot be made here.+        if (bufferImageGranularity > 1)+        {+            for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; )+            {+                const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex];+                if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity))+                {+                    if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type))+                    {+                        return false;+                    }+                }+                else+                {+                    // Already on next page.+                    break;+                }+            }+        }++        // All tests passed: Success.+        pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1);+        // pAllocationRequest->item unused.+        pAllocationRequest->type = VmaAllocationRequestType::UpperAddress;+        return true;+    }++    return false;+}+#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS+#endif // _VMA_BLOCK_METADATA_LINEAR++#ifndef _VMA_BLOCK_METADATA_TLSF+// To not search current larger region if first allocation won't succeed and skip to smaller range+// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest().+// When fragmentation and reusal of previous blocks doesn't matter then use with+// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible.+class VmaBlockMetadata_TLSF : public VmaBlockMetadata+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockMetadata_TLSF)+public:+    VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,+        VkDeviceSize bufferImageGranularity, bool isVirtual);+    ~VmaBlockMetadata_TLSF() override;++    size_t GetAllocationCount() const override { return m_AllocCount; }+    size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; }+    VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; }+    bool IsEmpty() const override { return m_NullBlock->offset == 0; }+    VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; }++    void Init(VkDeviceSize size) override;+    bool Validate() const override;++    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override;+    void AddStatistics(VmaStatistics& inoutStats) const override;++#if VMA_STATS_STRING_ENABLED+    void PrintDetailedMap(class VmaJsonWriter& json) const override;+#endif++    bool CreateAllocationRequest(+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        bool upperAddress,+        VmaSuballocationType allocType,+        uint32_t strategy,+        VmaAllocationRequest* pAllocationRequest) override;++    VkResult CheckCorruption(const void* pBlockData) override;+    void Alloc(+        const VmaAllocationRequest& request,+        VmaSuballocationType type,+        void* userData) override;++    void Free(VmaAllocHandle allocHandle) override;+    void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override;+    void* GetAllocationUserData(VmaAllocHandle allocHandle) const override;+    VmaAllocHandle GetAllocationListBegin() const override;+    VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override;+    VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override;+    void Clear() override;+    void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override;+    void DebugLogAllAllocations() const override;++private:+    // According to original paper it should be preferable 4 or 5:+    // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems"+    // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf+    static const uint8_t SECOND_LEVEL_INDEX = 5;+    static const uint16_t SMALL_BUFFER_SIZE = 256;+    static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16;+    static const uint8_t MEMORY_CLASS_SHIFT = 7;+    static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT;++    class Block+    {+    public:+        VkDeviceSize offset;+        VkDeviceSize size;+        Block* prevPhysical;+        Block* nextPhysical;++        void MarkFree() { prevFree = VMA_NULL; }+        void MarkTaken() { prevFree = this; }+        bool IsFree() const { return prevFree != this; }+        void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; }+        Block*& PrevFree() { return prevFree; }+        Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; }++    private:+        Block* prevFree; // Address of the same block here indicates that block is taken+        union+        {+            Block* nextFree;+            void* userData;+        };+    };++    size_t m_AllocCount;+    // Total number of free blocks besides null block+    size_t m_BlocksFreeCount;+    // Total size of free blocks excluding null block+    VkDeviceSize m_BlocksFreeSize;+    uint32_t m_IsFreeBitmap;+    uint8_t m_MemoryClasses;+    uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES];+    uint32_t m_ListsCount;+    /*+    * 0: 0-3 lists for small buffers+    * 1+: 0-(2^SLI-1) lists for normal buffers+    */+    Block** m_FreeList;+    VmaPoolAllocator<Block> m_BlockAllocator;+    Block* m_NullBlock;+    VmaBlockBufferImageGranularity m_GranularityHandler;++    static uint8_t SizeToMemoryClass(VkDeviceSize size);+    uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const;+    uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const;+    uint32_t GetListIndex(VkDeviceSize size) const;++    void RemoveFreeBlock(Block* block);+    void InsertFreeBlock(Block* block);+    void MergeBlock(Block* block, Block* prev);++    Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const;+    bool CheckBlock(+        Block& block,+        uint32_t listIndex,+        VkDeviceSize allocSize,+        VkDeviceSize allocAlignment,+        VmaSuballocationType allocType,+        VmaAllocationRequest* pAllocationRequest);+};++#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS+VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks,+    VkDeviceSize bufferImageGranularity, bool isVirtual)+    : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual),+    m_AllocCount(0),+    m_BlocksFreeCount(0),+    m_BlocksFreeSize(0),+    m_IsFreeBitmap(0),+    m_MemoryClasses(0),+    m_ListsCount(0),+    m_FreeList(VMA_NULL),+    m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT),+    m_NullBlock(VMA_NULL),+    m_GranularityHandler(bufferImageGranularity) {}++VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF()+{+    if (m_FreeList)+        vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount);+    m_GranularityHandler.Destroy(GetAllocationCallbacks());+}++void VmaBlockMetadata_TLSF::Init(VkDeviceSize size)+{+    VmaBlockMetadata::Init(size);++    if (!IsVirtual())+        m_GranularityHandler.Init(GetAllocationCallbacks(), size);++    m_NullBlock = m_BlockAllocator.Alloc();+    m_NullBlock->size = size;+    m_NullBlock->offset = 0;+    m_NullBlock->prevPhysical = VMA_NULL;+    m_NullBlock->nextPhysical = VMA_NULL;+    m_NullBlock->MarkFree();+    m_NullBlock->NextFree() = VMA_NULL;+    m_NullBlock->PrevFree() = VMA_NULL;+    uint8_t memoryClass = SizeToMemoryClass(size);+    uint16_t sli = SizeToSecondIndex(size, memoryClass);+    m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1;+    if (IsVirtual())+        m_ListsCount += 1UL << SECOND_LEVEL_INDEX;+    else+        m_ListsCount += 4;++    m_MemoryClasses = memoryClass + uint8_t(2);+    memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t));++    m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount);+    memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));+}++bool VmaBlockMetadata_TLSF::Validate() const+{+    VMA_VALIDATE(GetSumFreeSize() <= GetSize());++    VkDeviceSize calculatedSize = m_NullBlock->size;+    VkDeviceSize calculatedFreeSize = m_NullBlock->size;+    size_t allocCount = 0;+    size_t freeCount = 0;++    // Check integrity of free lists+    for (uint32_t list = 0; list < m_ListsCount; ++list)+    {+        Block* block = m_FreeList[list];+        if (block != VMA_NULL)+        {+            VMA_VALIDATE(block->IsFree());+            VMA_VALIDATE(block->PrevFree() == VMA_NULL);+            while (block->NextFree())+            {+                VMA_VALIDATE(block->NextFree()->IsFree());+                VMA_VALIDATE(block->NextFree()->PrevFree() == block);+                block = block->NextFree();+            }+        }+    }++    VkDeviceSize nextOffset = m_NullBlock->offset;+    auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual());++    VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL);+    if (m_NullBlock->prevPhysical)+    {+        VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock);+    }+    // Check all blocks+    for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical)+    {+        VMA_VALIDATE(prev->offset + prev->size == nextOffset);+        nextOffset = prev->offset;+        calculatedSize += prev->size;++        uint32_t listIndex = GetListIndex(prev->size);+        if (prev->IsFree())+        {+            ++freeCount;+            // Check if free block belongs to free list+            Block* freeBlock = m_FreeList[listIndex];+            VMA_VALIDATE(freeBlock != VMA_NULL);++            bool found = false;+            do+            {+                if (freeBlock == prev)+                    found = true;++                freeBlock = freeBlock->NextFree();+            } while (!found && freeBlock != VMA_NULL);++            VMA_VALIDATE(found);+            calculatedFreeSize += prev->size;+        }+        else+        {+            ++allocCount;+            // Check if taken block is not on a free list+            Block* freeBlock = m_FreeList[listIndex];+            while (freeBlock)+            {+                VMA_VALIDATE(freeBlock != prev);+                freeBlock = freeBlock->NextFree();+            }++            if (!IsVirtual())+            {+                VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size));+            }+        }++        if (prev->prevPhysical)+        {+            VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev);+        }+    }++    if (!IsVirtual())+    {+        VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx));+    }++    VMA_VALIDATE(nextOffset == 0);+    VMA_VALIDATE(calculatedSize == GetSize());+    VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize());+    VMA_VALIDATE(allocCount == m_AllocCount);+    VMA_VALIDATE(freeCount == m_BlocksFreeCount);++    return true;+}++void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const+{+    inoutStats.statistics.blockCount++;+    inoutStats.statistics.blockBytes += GetSize();+    if (m_NullBlock->size > 0)+        VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size);++    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)+    {+        if (block->IsFree())+            VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size);+        else+            VmaAddDetailedStatisticsAllocation(inoutStats, block->size);+    }+}++void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const+{+    inoutStats.blockCount++;+    inoutStats.allocationCount += (uint32_t)m_AllocCount;+    inoutStats.blockBytes += GetSize();+    inoutStats.allocationBytes += GetSize() - GetSumFreeSize();+}++#if VMA_STATS_STRING_ENABLED+void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const+{+    size_t blockCount = m_AllocCount + m_BlocksFreeCount;+    VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());+    VmaVector<Block*, VmaStlAllocator<Block*>> blockList(blockCount, allocator);++    size_t i = blockCount;+    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)+    {+        blockList[--i] = block;+    }+    VMA_ASSERT(i == 0);++    VmaDetailedStatistics stats;+    VmaClearDetailedStatistics(stats);+    AddDetailedStatistics(stats);++    PrintDetailedMap_Begin(json,+        stats.statistics.blockBytes - stats.statistics.allocationBytes,+        stats.statistics.allocationCount,+        stats.unusedRangeCount);++    for (; i < blockCount; ++i)+    {+        Block* block = blockList[i];+        if (block->IsFree())+            PrintDetailedMap_UnusedRange(json, block->offset, block->size);+        else+            PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData());+    }+    if (m_NullBlock->size > 0)+        PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size);++    PrintDetailedMap_End(json);+}+#endif++bool VmaBlockMetadata_TLSF::CreateAllocationRequest(+    VkDeviceSize allocSize,+    VkDeviceSize allocAlignment,+    bool upperAddress,+    VmaSuballocationType allocType,+    uint32_t strategy,+    VmaAllocationRequest* pAllocationRequest)+{+    VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!");+    VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm.");++    // For small granularity round up+    if (!IsVirtual())+        m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment);++    allocSize += GetDebugMargin();+    // Quick check for too small pool+    if (allocSize > GetSumFreeSize())+        return false;++    // If no free blocks in pool then check only null block+    if (m_BlocksFreeCount == 0)+        return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest);++    // Round up to the next block+    VkDeviceSize sizeForNextList = allocSize;+    VkDeviceSize smallSizeStep = VkDeviceSize(SMALL_BUFFER_SIZE / (IsVirtual() ? 1U << SECOND_LEVEL_INDEX : 4U));+    if (allocSize > SMALL_BUFFER_SIZE)+    {+        sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX));+    }+    else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep)+        sizeForNextList = SMALL_BUFFER_SIZE + 1;+    else+        sizeForNextList += smallSizeStep;++    uint32_t nextListIndex = m_ListsCount;+    uint32_t prevListIndex = m_ListsCount;+    Block* nextListBlock = VMA_NULL;+    Block* prevListBlock = VMA_NULL;++    // Check blocks according to strategies+    if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT)+    {+        // Quick check for larger block first+        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);+        if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+            return true;++        // If not fitted then null block+        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))+            return true;++        // Null block failed, search larger bucket+        while (nextListBlock)+        {+            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            nextListBlock = nextListBlock->NextFree();+        }++        // Failed again, check best fit bucket+        prevListBlock = FindFreeBlock(allocSize, prevListIndex);+        while (prevListBlock)+        {+            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            prevListBlock = prevListBlock->NextFree();+        }+    }+    else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT)+    {+        // Check best fit bucket+        prevListBlock = FindFreeBlock(allocSize, prevListIndex);+        while (prevListBlock)+        {+            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            prevListBlock = prevListBlock->NextFree();+        }++        // If failed check null block+        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))+            return true;++        // Check larger bucket+        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);+        while (nextListBlock)+        {+            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            nextListBlock = nextListBlock->NextFree();+        }+    }+    else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )+    {+        // Perform search from the start+        VmaStlAllocator<Block*> allocator(GetAllocationCallbacks());+        VmaVector<Block*, VmaStlAllocator<Block*>> blockList(m_BlocksFreeCount, allocator);++        size_t i = m_BlocksFreeCount;+        for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)+        {+            if (block->IsFree() && block->size >= allocSize)+                blockList[--i] = block;+        }++        for (; i < m_BlocksFreeCount; ++i)+        {+            Block& block = *blockList[i];+            if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+        }++        // If failed check null block+        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))+            return true;++        // Whole range searched, no more memory+        return false;+    }+    else+    {+        // Check larger bucket+        nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex);+        while (nextListBlock)+        {+            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            nextListBlock = nextListBlock->NextFree();+        }++        // If failed check null block+        if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest))+            return true;++        // Check best fit bucket+        prevListBlock = FindFreeBlock(allocSize, prevListIndex);+        while (prevListBlock)+        {+            if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            prevListBlock = prevListBlock->NextFree();+        }+    }++    // Worst case, full search has to be done+    while (++nextListIndex < m_ListsCount)+    {+        nextListBlock = m_FreeList[nextListIndex];+        while (nextListBlock)+        {+            if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest))+                return true;+            nextListBlock = nextListBlock->NextFree();+        }+    }++    // No more memory sadly+    return false;+}++VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData)+{+    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)+    {+        if (!block->IsFree())+        {+            if (!VmaValidateMagicValue(pBlockData, block->offset + block->size))+            {+                VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!");+                return VK_ERROR_UNKNOWN_COPY;+            }+        }+    }++    return VK_SUCCESS;+}++void VmaBlockMetadata_TLSF::Alloc(+    const VmaAllocationRequest& request,+    VmaSuballocationType type,+    void* userData)+{+    VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF);++    // Get block and pop it from the free list+    Block* currentBlock = (Block*)request.allocHandle;+    VkDeviceSize offset = request.algorithmData;+    VMA_ASSERT(currentBlock != VMA_NULL);+    VMA_ASSERT(currentBlock->offset <= offset);++    if (currentBlock != m_NullBlock)+        RemoveFreeBlock(currentBlock);++    VkDeviceSize debugMargin = GetDebugMargin();+    VkDeviceSize missingAlignment = offset - currentBlock->offset;++    // Append missing alignment to prev block or create new one+    if (missingAlignment)+    {+        Block* prevBlock = currentBlock->prevPhysical;+        VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!");++        if (prevBlock->IsFree() && prevBlock->size != debugMargin)+        {+            uint32_t oldList = GetListIndex(prevBlock->size);+            prevBlock->size += missingAlignment;+            // Check if new size crosses list bucket+            if (oldList != GetListIndex(prevBlock->size))+            {+                prevBlock->size -= missingAlignment;+                RemoveFreeBlock(prevBlock);+                prevBlock->size += missingAlignment;+                InsertFreeBlock(prevBlock);+            }+            else+                m_BlocksFreeSize += missingAlignment;+        }+        else+        {+            Block* newBlock = m_BlockAllocator.Alloc();+            currentBlock->prevPhysical = newBlock;+            prevBlock->nextPhysical = newBlock;+            newBlock->prevPhysical = prevBlock;+            newBlock->nextPhysical = currentBlock;+            newBlock->size = missingAlignment;+            newBlock->offset = currentBlock->offset;+            newBlock->MarkTaken();++            InsertFreeBlock(newBlock);+        }++        currentBlock->size -= missingAlignment;+        currentBlock->offset += missingAlignment;+    }++    VkDeviceSize size = request.size + debugMargin;+    if (currentBlock->size == size)+    {+        if (currentBlock == m_NullBlock)+        {+            // Setup new null block+            m_NullBlock = m_BlockAllocator.Alloc();+            m_NullBlock->size = 0;+            m_NullBlock->offset = currentBlock->offset + size;+            m_NullBlock->prevPhysical = currentBlock;+            m_NullBlock->nextPhysical = VMA_NULL;+            m_NullBlock->MarkFree();+            m_NullBlock->PrevFree() = VMA_NULL;+            m_NullBlock->NextFree() = VMA_NULL;+            currentBlock->nextPhysical = m_NullBlock;+            currentBlock->MarkTaken();+        }+    }+    else+    {+        VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!");++        // Create new free block+        Block* newBlock = m_BlockAllocator.Alloc();+        newBlock->size = currentBlock->size - size;+        newBlock->offset = currentBlock->offset + size;+        newBlock->prevPhysical = currentBlock;+        newBlock->nextPhysical = currentBlock->nextPhysical;+        currentBlock->nextPhysical = newBlock;+        currentBlock->size = size;++        if (currentBlock == m_NullBlock)+        {+            m_NullBlock = newBlock;+            m_NullBlock->MarkFree();+            m_NullBlock->NextFree() = VMA_NULL;+            m_NullBlock->PrevFree() = VMA_NULL;+            currentBlock->MarkTaken();+        }+        else+        {+            newBlock->nextPhysical->prevPhysical = newBlock;+            newBlock->MarkTaken();+            InsertFreeBlock(newBlock);+        }+    }+    currentBlock->UserData() = userData;++    if (debugMargin > 0)+    {+        currentBlock->size -= debugMargin;+        Block* newBlock = m_BlockAllocator.Alloc();+        newBlock->size = debugMargin;+        newBlock->offset = currentBlock->offset + currentBlock->size;+        newBlock->prevPhysical = currentBlock;+        newBlock->nextPhysical = currentBlock->nextPhysical;+        newBlock->MarkTaken();+        currentBlock->nextPhysical->prevPhysical = newBlock;+        currentBlock->nextPhysical = newBlock;+        InsertFreeBlock(newBlock);+    }++    if (!IsVirtual())+        m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData,+            currentBlock->offset, currentBlock->size);+    ++m_AllocCount;+}++void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle)+{+    Block* block = (Block*)allocHandle;+    Block* next = block->nextPhysical;+    VMA_ASSERT(!block->IsFree() && "Block is already free!");++    if (!IsVirtual())+        m_GranularityHandler.FreePages(block->offset, block->size);+    --m_AllocCount;++    VkDeviceSize debugMargin = GetDebugMargin();+    if (debugMargin > 0)+    {+        RemoveFreeBlock(next);+        MergeBlock(next, block);+        block = next;+        next = next->nextPhysical;+    }++    // Try merging+    Block* prev = block->prevPhysical;+    if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin)+    {+        RemoveFreeBlock(prev);+        MergeBlock(block, prev);+    }++    if (!next->IsFree())+        InsertFreeBlock(block);+    else if (next == m_NullBlock)+        MergeBlock(m_NullBlock, block);+    else+    {+        RemoveFreeBlock(next);+        MergeBlock(next, block);+        InsertFreeBlock(next);+    }+}++void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo)+{+    Block* block = (Block*)allocHandle;+    VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!");+    outInfo.offset = block->offset;+    outInfo.size = block->size;+    outInfo.pUserData = block->UserData();+}++void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const+{+    Block* block = (Block*)allocHandle;+    VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!");+    return block->UserData();+}++VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const+{+    if (m_AllocCount == 0)+        return VK_NULL_HANDLE;++    for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical)+    {+        if (!block->IsFree())+            return (VmaAllocHandle)block;+    }+    VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!");+    return VK_NULL_HANDLE;+}++VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const+{+    Block* startBlock = (Block*)prevAlloc;+    VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!");++    for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical)+    {+        if (!block->IsFree())+            return (VmaAllocHandle)block;+    }+    return VK_NULL_HANDLE;+}++VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const+{+    Block* block = (Block*)alloc;+    VMA_ASSERT(!block->IsFree() && "Incorrect block!");++    if (block->prevPhysical)+        return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0;+    return 0;+}++void VmaBlockMetadata_TLSF::Clear()+{+    m_AllocCount = 0;+    m_BlocksFreeCount = 0;+    m_BlocksFreeSize = 0;+    m_IsFreeBitmap = 0;+    m_NullBlock->offset = 0;+    m_NullBlock->size = GetSize();+    Block* block = m_NullBlock->prevPhysical;+    m_NullBlock->prevPhysical = VMA_NULL;+    while (block)+    {+        Block* prev = block->prevPhysical;+        m_BlockAllocator.Free(block);+        block = prev;+    }+    memset(m_FreeList, 0, m_ListsCount * sizeof(Block*));+    memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t));+    m_GranularityHandler.Clear();+}++void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData)+{+    Block* block = (Block*)allocHandle;+    VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!");+    block->UserData() = userData;+}++void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const+{+    for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical)+        if (!block->IsFree())+            DebugLogAllocation(block->offset, block->size, block->UserData());+}++uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size)+{+    if (size > SMALL_BUFFER_SIZE)+        return uint8_t(VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT);+    return 0;+}++uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const+{+    if (memoryClass == 0)+    {+        if (IsVirtual())+            return static_cast<uint16_t>((size - 1) / 8);+        return static_cast<uint16_t>((size - 1) / 64);+    }+    return static_cast<uint16_t>((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX));+}++uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const+{+    if (memoryClass == 0)+        return secondIndex;++    const uint32_t index = static_cast<uint32_t>(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex;+    if (IsVirtual())+        return index + (1 << SECOND_LEVEL_INDEX);+    return index + 4;+}++uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const+{+    uint8_t memoryClass = SizeToMemoryClass(size);+    return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass));+}++void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block)+{+    VMA_ASSERT(block != m_NullBlock);+    VMA_ASSERT(block->IsFree());++    if (block->NextFree() != VMA_NULL)+        block->NextFree()->PrevFree() = block->PrevFree();+    if (block->PrevFree() != VMA_NULL)+        block->PrevFree()->NextFree() = block->NextFree();+    else+    {+        uint8_t memClass = SizeToMemoryClass(block->size);+        uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);+        uint32_t index = GetListIndex(memClass, secondIndex);+        VMA_ASSERT(m_FreeList[index] == block);+        m_FreeList[index] = block->NextFree();+        if (block->NextFree() == VMA_NULL)+        {+            m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex);+            if (m_InnerIsFreeBitmap[memClass] == 0)+                m_IsFreeBitmap &= ~(1UL << memClass);+        }+    }+    block->MarkTaken();+    block->UserData() = VMA_NULL;+    --m_BlocksFreeCount;+    m_BlocksFreeSize -= block->size;+}++void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block)+{+    VMA_ASSERT(block != m_NullBlock);+    VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!");++    uint8_t memClass = SizeToMemoryClass(block->size);+    uint16_t secondIndex = SizeToSecondIndex(block->size, memClass);+    uint32_t index = GetListIndex(memClass, secondIndex);+    VMA_ASSERT(index < m_ListsCount);+    block->PrevFree() = VMA_NULL;+    block->NextFree() = m_FreeList[index];+    m_FreeList[index] = block;+    if (block->NextFree() != VMA_NULL)+        block->NextFree()->PrevFree() = block;+    else+    {+        m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex;+        m_IsFreeBitmap |= 1UL << memClass;+    }+    ++m_BlocksFreeCount;+    m_BlocksFreeSize += block->size;+}++void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev)+{+    VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!");+    VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!");++    block->offset = prev->offset;+    block->size += prev->size;+    block->prevPhysical = prev->prevPhysical;+    if (block->prevPhysical)+        block->prevPhysical->nextPhysical = block;+    m_BlockAllocator.Free(prev);+}++VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const+{+    uint8_t memoryClass = SizeToMemoryClass(size);+    uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass));+    if (!innerFreeMap)+    {+        // Check higher levels for available blocks+        uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1));+        if (!freeMap)+            return VMA_NULL; // No more memory available++        // Find lowest free region+        memoryClass = VMA_BITSCAN_LSB(freeMap);+        innerFreeMap = m_InnerIsFreeBitmap[memoryClass];+        VMA_ASSERT(innerFreeMap != 0);+    }+    // Find lowest free subregion+    listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap));+    VMA_ASSERT(m_FreeList[listIndex]);+    return m_FreeList[listIndex];+}++bool VmaBlockMetadata_TLSF::CheckBlock(+    Block& block,+    uint32_t listIndex,+    VkDeviceSize allocSize,+    VkDeviceSize allocAlignment,+    VmaSuballocationType allocType,+    VmaAllocationRequest* pAllocationRequest)+{+    VMA_ASSERT(block.IsFree() && "Block is already taken!");++    VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment);+    if (block.size < allocSize + alignedOffset - block.offset)+        return false;++    // Check for granularity conflicts+    if (!IsVirtual() &&+        m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType))+        return false;++    // Alloc successful+    pAllocationRequest->type = VmaAllocationRequestType::TLSF;+    pAllocationRequest->allocHandle = (VmaAllocHandle)&block;+    pAllocationRequest->size = allocSize - GetDebugMargin();+    pAllocationRequest->customData = (void*)allocType;+    pAllocationRequest->algorithmData = alignedOffset;++    // Place block at the start of list if it's normal block+    if (listIndex != m_ListsCount && block.PrevFree())+    {+        block.PrevFree()->NextFree() = block.NextFree();+        if (block.NextFree())+            block.NextFree()->PrevFree() = block.PrevFree();+        block.PrevFree() = VMA_NULL;+        block.NextFree() = m_FreeList[listIndex];+        m_FreeList[listIndex] = &block;+        if (block.NextFree())+            block.NextFree()->PrevFree() = &block;+    }++    return true;+}+#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS+#endif // _VMA_BLOCK_METADATA_TLSF++#ifndef _VMA_BLOCK_VECTOR+/*+Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific+Vulkan memory type.++Synchronized internally with a mutex.+*/+class VmaBlockVector+{+    friend struct VmaDefragmentationContext_T;+    VMA_CLASS_NO_COPY_NO_MOVE(VmaBlockVector)+public:+    VmaBlockVector(+        VmaAllocator hAllocator,+        VmaPool hParentPool,+        uint32_t memoryTypeIndex,+        VkDeviceSize preferredBlockSize,+        size_t minBlockCount,+        size_t maxBlockCount,+        VkDeviceSize bufferImageGranularity,+        bool explicitBlockSize,+        uint32_t algorithm,+        float priority,+        VkDeviceSize minAllocationAlignment,+        void* pMemoryAllocateNext);+    ~VmaBlockVector();++    VmaAllocator GetAllocator() const { return m_hAllocator; }+    VmaPool GetParentPool() const { return m_hParentPool; }+    bool IsCustomPool() const { return m_hParentPool != VMA_NULL; }+    uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; }+    VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; }+    VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; }+    uint32_t GetAlgorithm() const { return m_Algorithm; }+    bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; }+    float GetPriority() const { return m_Priority; }+    const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; }+    // To be used only while the m_Mutex is locked. Used during defragmentation.+    size_t GetBlockCount() const { return m_Blocks.size(); }+    // To be used only while the m_Mutex is locked. Used during defragmentation.+    VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; }+    VMA_RW_MUTEX &GetMutex() { return m_Mutex; }++    VkResult CreateMinBlocks();+    void AddStatistics(VmaStatistics& inoutStats);+    void AddDetailedStatistics(VmaDetailedStatistics& inoutStats);+    bool IsEmpty();+    bool IsCorruptionDetectionEnabled() const;++    VkResult Allocate(+        VkDeviceSize size,+        VkDeviceSize alignment,+        const VmaAllocationCreateInfo& createInfo,+        VmaSuballocationType suballocType,+        size_t allocationCount,+        VmaAllocation* pAllocations);++    void Free(VmaAllocation hAllocation);++#if VMA_STATS_STRING_ENABLED+    void PrintDetailedMap(class VmaJsonWriter& json);+#endif++    VkResult CheckCorruption();++private:+    const VmaAllocator m_hAllocator;+    const VmaPool m_hParentPool;+    const uint32_t m_MemoryTypeIndex;+    const VkDeviceSize m_PreferredBlockSize;+    const size_t m_MinBlockCount;+    const size_t m_MaxBlockCount;+    const VkDeviceSize m_BufferImageGranularity;+    const bool m_ExplicitBlockSize;+    const uint32_t m_Algorithm;+    const float m_Priority;+    const VkDeviceSize m_MinAllocationAlignment;++    void* const m_pMemoryAllocateNext;+    VMA_RW_MUTEX m_Mutex;+    // Incrementally sorted by sumFreeSize, ascending.+    VmaVector<VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*>> m_Blocks;+    uint32_t m_NextBlockId;+    bool m_IncrementalSort = true;++    void SetIncrementalSort(bool val) { m_IncrementalSort = val; }++    VkDeviceSize CalcMaxBlockSize() const;+    // Finds and removes given block from vector.+    void Remove(VmaDeviceMemoryBlock* pBlock);+    // Performs single step in sorting m_Blocks. They may not be fully sorted+    // after this call.+    void IncrementallySortBlocks();+    void SortByFreeSize();++    VkResult AllocatePage(+        VkDeviceSize size,+        VkDeviceSize alignment,+        const VmaAllocationCreateInfo& createInfo,+        VmaSuballocationType suballocType,+        VmaAllocation* pAllocation);++    VkResult AllocateFromBlock(+        VmaDeviceMemoryBlock* pBlock,+        VkDeviceSize size,+        VkDeviceSize alignment,+        VmaAllocationCreateFlags allocFlags,+        void* pUserData,+        VmaSuballocationType suballocType,+        uint32_t strategy,+        VmaAllocation* pAllocation);++    VkResult CommitAllocationRequest(+        VmaAllocationRequest& allocRequest,+        VmaDeviceMemoryBlock* pBlock,+        VkDeviceSize alignment,+        VmaAllocationCreateFlags allocFlags,+        void* pUserData,+        VmaSuballocationType suballocType,+        VmaAllocation* pAllocation);++    VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);+    bool HasEmptyBlock();+};+#endif // _VMA_BLOCK_VECTOR++#ifndef _VMA_DEFRAGMENTATION_CONTEXT+struct VmaDefragmentationContext_T+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaDefragmentationContext_T)+public:+    VmaDefragmentationContext_T(+        VmaAllocator hAllocator,+        const VmaDefragmentationInfo& info);+    ~VmaDefragmentationContext_T();++    void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; }++    VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo);+    VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo);++private:+    // Max number of allocations to ignore due to size constraints before ending single pass+    static const uint8_t MAX_ALLOCS_TO_IGNORE = 16;+    enum class CounterStatus { Pass, Ignore, End };++    struct FragmentedBlock+    {+        uint32_t data;+        VmaDeviceMemoryBlock* block;+    };+    struct StateBalanced+    {+        VkDeviceSize avgFreeSize = 0;+        VkDeviceSize avgAllocSize = UINT64_MAX;+    };+    struct StateExtensive+    {+        enum class Operation : uint8_t+        {+            FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll,+            MoveBuffers, MoveTextures, MoveAll,+            Cleanup, Done+        };++        Operation operation = Operation::FindFreeBlockTexture;+        size_t firstFreeBlock = SIZE_MAX;+    };+    struct MoveAllocationData+    {+        VkDeviceSize size;+        VkDeviceSize alignment;+        VmaSuballocationType type;+        VmaAllocationCreateFlags flags;+        VmaDefragmentationMove move = {};+    };++    const VkDeviceSize m_MaxPassBytes;+    const uint32_t m_MaxPassAllocations;+    const PFN_vmaCheckDefragmentationBreakFunction m_BreakCallback;+    void* m_BreakCallbackUserData;++    VmaStlAllocator<VmaDefragmentationMove> m_MoveAllocator;+    VmaVector<VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove>> m_Moves;++    uint8_t m_IgnoredAllocs = 0;+    uint32_t m_Algorithm;+    uint32_t m_BlockVectorCount;+    VmaBlockVector* m_PoolBlockVector;+    VmaBlockVector** m_pBlockVectors;+    size_t m_ImmovableBlockCount = 0;+    VmaDefragmentationStats m_GlobalStats = { 0 };+    VmaDefragmentationStats m_PassStats = { 0 };+    void* m_AlgorithmState = VMA_NULL;++    static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata);+    CounterStatus CheckCounters(VkDeviceSize bytes);+    bool IncrementCounters(VkDeviceSize bytes);+    bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block);+    bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector);++    bool ComputeDefragmentation(VmaBlockVector& vector, size_t index);+    bool ComputeDefragmentation_Fast(VmaBlockVector& vector);+    bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update);+    bool ComputeDefragmentation_Full(VmaBlockVector& vector);+    bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index);++    static void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state);+    bool MoveDataToFreeBlocks(VmaSuballocationType currentType,+        VmaBlockVector& vector, size_t firstFreeBlock,+        bool& texturePresent, bool& bufferPresent, bool& otherPresent);+};+#endif // _VMA_DEFRAGMENTATION_CONTEXT++#ifndef _VMA_POOL_T+struct VmaPool_T+{+    friend struct VmaPoolListItemTraits;+    VMA_CLASS_NO_COPY_NO_MOVE(VmaPool_T)+public:+    VmaBlockVector m_BlockVector;+    VmaDedicatedAllocationList m_DedicatedAllocations;++    VmaPool_T(+        VmaAllocator hAllocator,+        const VmaPoolCreateInfo& createInfo,+        VkDeviceSize preferredBlockSize);+    ~VmaPool_T();++    uint32_t GetId() const { return m_Id; }+    void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; }++    const char* GetName() const { return m_Name; }+    void SetName(const char* pName);++#if VMA_STATS_STRING_ENABLED+    //void PrintDetailedMap(class VmaStringBuilder& sb);+#endif++private:+    uint32_t m_Id;+    char* m_Name;+    VmaPool_T* m_PrevPool = VMA_NULL;+    VmaPool_T* m_NextPool = VMA_NULL;+};++struct VmaPoolListItemTraits+{+    typedef VmaPool_T ItemType;++    static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; }+    static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; }+    static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; }+    static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; }+};+#endif // _VMA_POOL_T++#ifndef _VMA_CURRENT_BUDGET_DATA+struct VmaCurrentBudgetData+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaCurrentBudgetData)+public:++    VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS];+    VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS];+    VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS];+    VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS];++#if VMA_MEMORY_BUDGET+    VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch;+    VMA_RW_MUTEX m_BudgetMutex;+    uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS];+    uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS];+    uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS];+#endif // VMA_MEMORY_BUDGET++    VmaCurrentBudgetData();++    void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);+    void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize);+};++#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS+VmaCurrentBudgetData::VmaCurrentBudgetData()+{+    for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex)+    {+        m_BlockCount[heapIndex] = 0;+        m_AllocationCount[heapIndex] = 0;+        m_BlockBytes[heapIndex] = 0;+        m_AllocationBytes[heapIndex] = 0;+#if VMA_MEMORY_BUDGET+        m_VulkanUsage[heapIndex] = 0;+        m_VulkanBudget[heapIndex] = 0;+        m_BlockBytesAtBudgetFetch[heapIndex] = 0;+#endif+    }++#if VMA_MEMORY_BUDGET+    m_OperationsSinceBudgetFetch = 0;+#endif+}++void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)+{+    m_AllocationBytes[heapIndex] += allocationSize;+    ++m_AllocationCount[heapIndex];+#if VMA_MEMORY_BUDGET+    ++m_OperationsSinceBudgetFetch;+#endif+}++void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize)+{+    VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize);+    m_AllocationBytes[heapIndex] -= allocationSize;+    VMA_ASSERT(m_AllocationCount[heapIndex] > 0);+    --m_AllocationCount[heapIndex];+#if VMA_MEMORY_BUDGET+    ++m_OperationsSinceBudgetFetch;+#endif+}+#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS+#endif // _VMA_CURRENT_BUDGET_DATA++#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR+/*+Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects.+*/+class VmaAllocationObjectAllocator+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocationObjectAllocator)+public:+    explicit VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks)+        : m_Allocator(pAllocationCallbacks, 1024) {}++    template<typename... Types> VmaAllocation Allocate(Types&&... args);+    void Free(VmaAllocation hAlloc);++private:+    VMA_MUTEX m_Mutex;+    VmaPoolAllocator<VmaAllocation_T> m_Allocator;+};++template<typename... Types>+VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args)+{+    VmaMutexLock mutexLock(m_Mutex);+    return m_Allocator.Alloc<Types...>(std::forward<Types>(args)...);+}++void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc)+{+    VmaMutexLock mutexLock(m_Mutex);+    m_Allocator.Free(hAlloc);+}+#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR++#ifndef _VMA_VIRTUAL_BLOCK_T+struct VmaVirtualBlock_T+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaVirtualBlock_T)+public:+    const bool m_AllocationCallbacksSpecified;+    const VkAllocationCallbacks m_AllocationCallbacks;++    explicit VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo);+    ~VmaVirtualBlock_T();++    bool IsEmpty() const { return m_Metadata->IsEmpty(); }+    void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); }+    void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); }+    void Clear() { m_Metadata->Clear(); }++    const VkAllocationCallbacks* GetAllocationCallbacks() const;+    void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo);+    VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,+        VkDeviceSize* outOffset);+    void GetStatistics(VmaStatistics& outStats) const;+    void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const;+#if VMA_STATS_STRING_ENABLED+    void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const;+#endif++private:+    VmaBlockMetadata* m_Metadata;+};++#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS+VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo)+    : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL),+    m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks)+{+    const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK;+    switch (algorithm)+    {+    case 0:+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);+        break;+    case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT:+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true);+        break;+    default:+        VMA_ASSERT(0);+        m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true);+    }++    m_Metadata->Init(createInfo.size);+}++VmaVirtualBlock_T::~VmaVirtualBlock_T()+{+    // Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT+    // to receive the list of the unfreed allocations.+    if (!m_Metadata->IsEmpty())+        m_Metadata->DebugLogAllAllocations();+    // This is the most important assert in the entire library.+    // Hitting it means you have some memory leak - unreleased virtual allocations.+    VMA_ASSERT_LEAK(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!");++    vma_delete(GetAllocationCallbacks(), m_Metadata);+}++const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const+{+    return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;+}++void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo)+{+    m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo);+}++VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation,+    VkDeviceSize* outOffset)+{+    VmaAllocationRequest request = {};+    if (m_Metadata->CreateAllocationRequest(+        createInfo.size, // allocSize+        VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment+        (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress+        VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant+        createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy+        &request))+    {+        m_Metadata->Alloc(request,+            VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant+            createInfo.pUserData);+        outAllocation = (VmaVirtualAllocation)request.allocHandle;+        if(outOffset)+            *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle);+        return VK_SUCCESS;+    }+    outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE;+    if (outOffset)+        *outOffset = UINT64_MAX;+    return VK_ERROR_OUT_OF_DEVICE_MEMORY;+}++void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const+{+    VmaClearStatistics(outStats);+    m_Metadata->AddStatistics(outStats);+}++void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const+{+    VmaClearDetailedStatistics(outStats);+    m_Metadata->AddDetailedStatistics(outStats);+}++#if VMA_STATS_STRING_ENABLED+void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const+{+    VmaJsonWriter json(GetAllocationCallbacks(), sb);+    json.BeginObject();++    VmaDetailedStatistics stats;+    CalculateDetailedStatistics(stats);++    json.WriteString("Stats");+    VmaPrintDetailedStatistics(json, stats);++    if (detailedMap)+    {+        json.WriteString("Details");+        json.BeginObject();+        m_Metadata->PrintDetailedMap(json);+        json.EndObject();+    }++    json.EndObject();+}+#endif // VMA_STATS_STRING_ENABLED+#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS+#endif // _VMA_VIRTUAL_BLOCK_T+++// Main allocator object.+struct VmaAllocator_T+{+    VMA_CLASS_NO_COPY_NO_MOVE(VmaAllocator_T)+public:+    const bool m_UseMutex;+    const uint32_t m_VulkanApiVersion;+    bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).+    bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0).+    bool m_UseExtMemoryBudget;+    bool m_UseAmdDeviceCoherentMemory;+    bool m_UseKhrBufferDeviceAddress;+    bool m_UseExtMemoryPriority;+    bool m_UseKhrMaintenance4;+    bool m_UseKhrMaintenance5;+    bool m_UseKhrExternalMemoryWin32;+    const VkDevice m_hDevice;+    const VkInstance m_hInstance;+    const bool m_AllocationCallbacksSpecified;+    const VkAllocationCallbacks m_AllocationCallbacks;+    VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;+    VmaAllocationObjectAllocator m_AllocationObjectAllocator;++    // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size.+    uint32_t m_HeapSizeLimitMask;++    VkPhysicalDeviceProperties m_PhysicalDeviceProperties;+    VkPhysicalDeviceMemoryProperties m_MemProps;++    // Default pools.+    VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];+    VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];++    VmaCurrentBudgetData m_Budget;+    VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects.++    explicit VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);+    VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo);+    ~VmaAllocator_T();++    const VkAllocationCallbacks* GetAllocationCallbacks() const+    {+        return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL;+    }+    const VmaVulkanFunctions& GetVulkanFunctions() const+    {+        return m_VulkanFunctions;+    }++    VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; }++    VkDeviceSize GetBufferImageGranularity() const+    {+        return VMA_MAX(+            static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),+            m_PhysicalDeviceProperties.limits.bufferImageGranularity);+    }++    uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }+    uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }++    uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const+    {+        VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount);+        return m_MemProps.memoryTypes[memTypeIndex].heapIndex;+    }+    // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT.+    bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const+    {+        return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) ==+            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+    }+    // Minimum alignment for all allocations in specific memory type.+    VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const+    {+        return IsMemoryTypeNonCoherent(memTypeIndex) ?+            VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) :+            (VkDeviceSize)VMA_MIN_ALIGNMENT;+    }++    bool IsIntegratedGpu() const+    {+        return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU;+    }++    uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; }++    void GetBufferMemoryRequirements(+        VkBuffer hBuffer,+        VkMemoryRequirements& memReq,+        bool& requiresDedicatedAllocation,+        bool& prefersDedicatedAllocation) const;+    void GetImageMemoryRequirements(+        VkImage hImage,+        VkMemoryRequirements& memReq,+        bool& requiresDedicatedAllocation,+        bool& prefersDedicatedAllocation) const;+    VkResult FindMemoryTypeIndex(+        uint32_t memoryTypeBits,+        const VmaAllocationCreateInfo* pAllocationCreateInfo,+        VmaBufferImageUsage bufImgUsage,+        uint32_t* pMemoryTypeIndex) const;++    // Main allocation function.+    VkResult AllocateMemory(+        const VkMemoryRequirements& vkMemReq,+        bool requiresDedicatedAllocation,+        bool prefersDedicatedAllocation,+        VkBuffer dedicatedBuffer,+        VkImage dedicatedImage,+        VmaBufferImageUsage dedicatedBufferImageUsage,+        const VmaAllocationCreateInfo& createInfo,+        VmaSuballocationType suballocType,+        size_t allocationCount,+        VmaAllocation* pAllocations);++    // Main deallocation function.+    void FreeMemory(+        size_t allocationCount,+        const VmaAllocation* pAllocations);++    void CalculateStatistics(VmaTotalStatistics* pStats);++    void GetHeapBudgets(+        VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount);++#if VMA_STATS_STRING_ENABLED+    void PrintDetailedMap(class VmaJsonWriter& json);+#endif++    static void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);+    static void GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo);++    VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool);+    void DestroyPool(VmaPool pool);+    static void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats);+    static void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats);++    void SetCurrentFrameIndex(uint32_t frameIndex);+    uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); }++    static VkResult CheckPoolCorruption(VmaPool hPool);+    VkResult CheckCorruption(uint32_t memoryTypeBits);++    // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping.+    VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory);+    // Call to Vulkan function vkFreeMemory with accompanying bookkeeping.+    void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory);+    // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR.+    VkResult BindVulkanBuffer(+        VkDeviceMemory memory,+        VkDeviceSize memoryOffset,+        VkBuffer buffer,+        const void* pNext) const;+    // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR.+    VkResult BindVulkanImage(+        VkDeviceMemory memory,+        VkDeviceSize memoryOffset,+        VkImage image,+        const void* pNext) const;++    VkResult Map(VmaAllocation hAllocation, void** ppData);+    void Unmap(VmaAllocation hAllocation);++    VkResult BindBufferMemory(+        VmaAllocation hAllocation,+        VkDeviceSize allocationLocalOffset,+        VkBuffer hBuffer,+        const void* pNext);+    VkResult BindImageMemory(+        VmaAllocation hAllocation,+        VkDeviceSize allocationLocalOffset,+        VkImage hImage,+        const void* pNext);++    VkResult FlushOrInvalidateAllocation(+        VmaAllocation hAllocation,+        VkDeviceSize offset, VkDeviceSize size,+        VMA_CACHE_OPERATION op);+    VkResult FlushOrInvalidateAllocations(+        uint32_t allocationCount,+        const VmaAllocation* allocations,+        const VkDeviceSize* offsets, const VkDeviceSize* sizes,+        VMA_CACHE_OPERATION op);++    VkResult CopyMemoryToAllocation(+        const void* pSrcHostPointer,+        VmaAllocation dstAllocation,+        VkDeviceSize dstAllocationLocalOffset,+        VkDeviceSize size);+    VkResult CopyAllocationToMemory(+        VmaAllocation srcAllocation,+        VkDeviceSize srcAllocationLocalOffset,+        void* pDstHostPointer,+        VkDeviceSize size);++    void FillAllocation(VmaAllocation hAllocation, uint8_t pattern);++    /*+    Returns bit mask of memory types that can support defragmentation on GPU as+    they support creation of required buffer for copy operations.+    */+    uint32_t GetGpuDefragmentationMemoryTypeBits();++#if VMA_EXTERNAL_MEMORY+    VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const+    {+        return m_TypeExternalMemoryHandleTypes[memTypeIndex];+    }+#endif // #if VMA_EXTERNAL_MEMORY++private:+    VkDeviceSize m_PreferredLargeHeapBlockSize;++    VkPhysicalDevice m_PhysicalDevice;+    VMA_ATOMIC_UINT32 m_CurrentFrameIndex;+    VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized.+#if VMA_EXTERNAL_MEMORY+    VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES];+#endif // #if VMA_EXTERNAL_MEMORY++    VMA_RW_MUTEX m_PoolsMutex;+    typedef VmaIntrusiveLinkedList<VmaPoolListItemTraits> PoolList;+    // Protected by m_PoolsMutex.+    PoolList m_Pools;+    uint32_t m_NextPoolId;++    VmaVulkanFunctions m_VulkanFunctions;++    // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types.+    uint32_t m_GlobalMemoryTypeBits;++    void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions);++#if VMA_STATIC_VULKAN_FUNCTIONS == 1+    void ImportVulkanFunctions_Static();+#endif++    void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions);++#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1+    void ImportVulkanFunctions_Dynamic();+#endif++    void ValidateVulkanFunctions() const;++    VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);++    VkResult AllocateMemoryOfType(+        VmaPool pool,+        VkDeviceSize size,+        VkDeviceSize alignment,+        bool dedicatedPreferred,+        VkBuffer dedicatedBuffer,+        VkImage dedicatedImage,+        VmaBufferImageUsage dedicatedBufferImageUsage,+        const VmaAllocationCreateInfo& createInfo,+        uint32_t memTypeIndex,+        VmaSuballocationType suballocType,+        VmaDedicatedAllocationList& dedicatedAllocations,+        VmaBlockVector& blockVector,+        size_t allocationCount,+        VmaAllocation* pAllocations);++    // Helper function only to be used inside AllocateDedicatedMemory.+    VkResult AllocateDedicatedMemoryPage(+        VmaPool pool,+        VkDeviceSize size,+        VmaSuballocationType suballocType,+        uint32_t memTypeIndex,+        const VkMemoryAllocateInfo& allocInfo,+        bool map,+        bool isUserDataString,+        bool isMappingAllowed,+        void* pUserData,+        VmaAllocation* pAllocation);++    // Allocates and registers new VkDeviceMemory specifically for dedicated allocations.+    VkResult AllocateDedicatedMemory(+        VmaPool pool,+        VkDeviceSize size,+        VmaSuballocationType suballocType,+        VmaDedicatedAllocationList& dedicatedAllocations,+        uint32_t memTypeIndex,+        bool map,+        bool isUserDataString,+        bool isMappingAllowed,+        bool canAliasMemory,+        void* pUserData,+        float priority,+        VkBuffer dedicatedBuffer,+        VkImage dedicatedImage,+        VmaBufferImageUsage dedicatedBufferImageUsage,+        size_t allocationCount,+        VmaAllocation* pAllocations,+        const void* pNextChain = VMA_NULL);++    void FreeDedicatedMemory(VmaAllocation allocation);++    VkResult CalcMemTypeParams(+        VmaAllocationCreateInfo& outCreateInfo,+        uint32_t memTypeIndex,+        VkDeviceSize size,+        size_t allocationCount);+    static VkResult CalcAllocationParams(+        VmaAllocationCreateInfo& outCreateInfo,+        bool dedicatedRequired);++    /*+    Calculates and returns bit mask of memory types that can support defragmentation+    on GPU as they support creation of required buffer for copy operations.+    */+    uint32_t CalculateGpuDefragmentationMemoryTypeBits() const;+    uint32_t CalculateGlobalMemoryTypeBits() const;++    bool GetFlushOrInvalidateRange(+        VmaAllocation allocation,+        VkDeviceSize offset, VkDeviceSize size,+        VkMappedMemoryRange& outRange) const;++#if VMA_MEMORY_BUDGET+    void UpdateVulkanBudget();+#endif // #if VMA_MEMORY_BUDGET+};+++#ifndef _VMA_MEMORY_FUNCTIONS+static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)+{+    return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);+}++static void VmaFree(VmaAllocator hAllocator, void* ptr)+{+    VmaFree(&hAllocator->m_AllocationCallbacks, ptr);+}++template<typename T>+static T* VmaAllocate(VmaAllocator hAllocator)+{+    return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));+}++template<typename T>+static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)+{+    return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));+}++template<typename T>+static void vma_delete(VmaAllocator hAllocator, T* ptr)+{+    if(ptr != VMA_NULL)+    {+        ptr->~T();+        VmaFree(hAllocator, ptr);+    }+}++template<typename T>+static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)+{+    if(ptr != VMA_NULL)+    {+        for(size_t i = count; i--; )+            ptr[i].~T();+        VmaFree(hAllocator, ptr);+    }+}+#endif // _VMA_MEMORY_FUNCTIONS++#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS+VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator)+    : m_pMetadata(VMA_NULL),+    m_hParentPool(nullptr),+    m_MemoryTypeIndex(UINT32_MAX),+    m_Id(0),+    m_hMemory(VK_NULL_HANDLE),+    m_MapCount(0),+    m_pMappedData(VMA_NULL){}++VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock()+{+    VMA_ASSERT_LEAK(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped.");+    VMA_ASSERT_LEAK(m_hMemory == VK_NULL_HANDLE);+}++void VmaDeviceMemoryBlock::Init(+    VmaAllocator hAllocator,+    VmaPool hParentPool,+    uint32_t newMemoryTypeIndex,+    VkDeviceMemory newMemory,+    VkDeviceSize newSize,+    uint32_t id,+    uint32_t algorithm,+    VkDeviceSize bufferImageGranularity)+{+    VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);++    m_hParentPool = hParentPool;+    m_MemoryTypeIndex = newMemoryTypeIndex;+    m_Id = id;+    m_hMemory = newMemory;++    switch (algorithm)+    {+    case 0:+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),+            bufferImageGranularity, false); // isVirtual+        break;+    case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT:+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(),+            bufferImageGranularity, false); // isVirtual+        break;+    default:+        VMA_ASSERT(0);+        m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(),+            bufferImageGranularity, false); // isVirtual+    }+    m_pMetadata->Init(newSize);+}++void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator)+{+    // Define macro VMA_DEBUG_LOG_FORMAT or more specialized VMA_LEAK_LOG_FORMAT+    // to receive the list of the unfreed allocations.+    if (!m_pMetadata->IsEmpty())+        m_pMetadata->DebugLogAllAllocations();+    // This is the most important assert in the entire library.+    // Hitting it means you have some memory leak - unreleased VmaAllocation objects.+    VMA_ASSERT_LEAK(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!");++    VMA_ASSERT_LEAK(m_hMemory != VK_NULL_HANDLE);+    allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory);+    m_hMemory = VK_NULL_HANDLE;++    vma_delete(allocator, m_pMetadata);+    m_pMetadata = VMA_NULL;+}++void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator)+{+    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    m_MappingHysteresis.PostAlloc();+}++void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator)+{+    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    if(m_MappingHysteresis.PostFree())+    {+        VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0);+        if (m_MapCount == 0)+        {+            m_pMappedData = VMA_NULL;+            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);+        }+    }+}++bool VmaDeviceMemoryBlock::Validate() const+{+    VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) &&+        (m_pMetadata->GetSize() != 0));++    return m_pMetadata->Validate();+}++VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)+{+    void* pData = VMA_NULL;+    VkResult res = Map(hAllocator, 1, &pData);+    if (res != VK_SUCCESS)+    {+        return res;+    }++    res = m_pMetadata->CheckCorruption(pData);++    Unmap(hAllocator, 1);++    return res;+}++VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData)+{+    if (count == 0)+    {+        return VK_SUCCESS;+    }++    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();+    if (oldTotalMapCount != 0)+    {+        VMA_ASSERT(m_pMappedData != VMA_NULL);+        m_MappingHysteresis.PostMap();+        m_MapCount += count;+        if (ppData != VMA_NULL)+        {+            *ppData = m_pMappedData;+        }+        return VK_SUCCESS;+    }++    VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(+        hAllocator->m_hDevice,+        m_hMemory,+        0, // offset+        VK_WHOLE_SIZE,+        0, // flags+        &m_pMappedData);+    if (result == VK_SUCCESS)+    {+        VMA_ASSERT(m_pMappedData != VMA_NULL);+        m_MappingHysteresis.PostMap();+        m_MapCount = count;+        if (ppData != VMA_NULL)+        {+            *ppData = m_pMappedData;+        }+    }+    return result;+}++void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count)+{+    if (count == 0)+    {+        return;+    }++    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    if (m_MapCount >= count)+    {+        m_MapCount -= count;+        const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping();+        if (totalMapCount == 0)+        {+            m_pMappedData = VMA_NULL;+            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory);+        }+        m_MappingHysteresis.PostUnmap();+    }+    else+    {+        VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped.");+    }+}++VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)+{+    VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);++    void* pData = VMA_NULL;+    VkResult res = Map(hAllocator, 1, &pData);+    if (res != VK_SUCCESS)+    {+        return res;+    }++    VmaWriteMagicValue(pData, allocOffset + allocSize);++    Unmap(hAllocator, 1);+    return VK_SUCCESS;+}++VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize)+{+    VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION);++    void* pData = VMA_NULL;+    VkResult res = Map(hAllocator, 1, &pData);+    if (res != VK_SUCCESS)+    {+        return res;+    }++    if (!VmaValidateMagicValue(pData, allocOffset + allocSize))+    {+        VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!");+    }++    Unmap(hAllocator, 1);+    return VK_SUCCESS;+}++VkResult VmaDeviceMemoryBlock::BindBufferMemory(+    VmaAllocator hAllocator,+    VmaAllocation hAllocation,+    VkDeviceSize allocationLocalOffset,+    VkBuffer hBuffer,+    const void* pNext)+{+    VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&+        hAllocation->GetBlock() == this);+    VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&+        "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");+    const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;+    // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.+    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext);+}++VkResult VmaDeviceMemoryBlock::BindImageMemory(+    VmaAllocator hAllocator,+    VmaAllocation hAllocation,+    VkDeviceSize allocationLocalOffset,+    VkImage hImage,+    const void* pNext)+{+    VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK &&+        hAllocation->GetBlock() == this);+    VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() &&+        "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?");+    const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset;+    // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads.+    VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex);+    return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext);+}++#if VMA_EXTERNAL_MEMORY_WIN32+VkResult VmaDeviceMemoryBlock::CreateWin32Handle(const VmaAllocator hAllocator, PFN_vkGetMemoryWin32HandleKHR pvkGetMemoryWin32HandleKHR, HANDLE hTargetProcess, HANDLE* pHandle) noexcept+{+    VMA_ASSERT(pHandle);+    return m_Handle.GetHandle(hAllocator->m_hDevice, m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);+}+#endif // VMA_EXTERNAL_MEMORY_WIN32+#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS++#ifndef _VMA_ALLOCATION_T_FUNCTIONS+VmaAllocation_T::VmaAllocation_T(bool mappingAllowed)+    : m_Alignment{ 1 },+    m_Size{ 0 },+    m_pUserData{ VMA_NULL },+    m_pName{ VMA_NULL },+    m_MemoryTypeIndex{ 0 },+    m_Type{ (uint8_t)ALLOCATION_TYPE_NONE },+    m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN },+    m_MapCount{ 0 },+    m_Flags{ 0 }+{+    if(mappingAllowed)+        m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED;+}++VmaAllocation_T::~VmaAllocation_T()+{+    VMA_ASSERT_LEAK(m_MapCount == 0 && "Allocation was not unmapped before destruction.");++    // Check if owned string was freed.+    VMA_ASSERT(m_pName == VMA_NULL);+}++void VmaAllocation_T::InitBlockAllocation(+    VmaDeviceMemoryBlock* block,+    VmaAllocHandle allocHandle,+    VkDeviceSize alignment,+    VkDeviceSize size,+    uint32_t memoryTypeIndex,+    VmaSuballocationType suballocationType,+    bool mapped)+{+    VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);+    VMA_ASSERT(block != VMA_NULL);+    m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK;+    m_Alignment = alignment;+    m_Size = size;+    m_MemoryTypeIndex = memoryTypeIndex;+    if(mapped)+    {+        VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");+        m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;+    }+    m_SuballocationType = (uint8_t)suballocationType;+    m_BlockAllocation.m_Block = block;+    m_BlockAllocation.m_AllocHandle = allocHandle;+}++void VmaAllocation_T::InitDedicatedAllocation(+    VmaAllocator allocator,+    VmaPool hParentPool,+    uint32_t memoryTypeIndex,+    VkDeviceMemory hMemory,+    VmaSuballocationType suballocationType,+    void* pMappedData,+    VkDeviceSize size)+{+    VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);+    VMA_ASSERT(hMemory != VK_NULL_HANDLE);+    m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED;+    m_Alignment = 0;+    m_Size = size;+    m_MemoryTypeIndex = memoryTypeIndex;+    m_SuballocationType = (uint8_t)suballocationType;+    m_DedicatedAllocation.m_ExtraData = VMA_NULL;+    m_DedicatedAllocation.m_hParentPool = hParentPool;+    m_DedicatedAllocation.m_hMemory = hMemory;+    m_DedicatedAllocation.m_Prev = VMA_NULL;+    m_DedicatedAllocation.m_Next = VMA_NULL;++    if (pMappedData != VMA_NULL)+    {+        VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");+        m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP;+        EnsureExtraData(allocator);+        m_DedicatedAllocation.m_ExtraData->m_pMappedData = pMappedData;+    }+}++void VmaAllocation_T::Destroy(VmaAllocator allocator)+{+    FreeName(allocator);++    if (GetType() == ALLOCATION_TYPE_DEDICATED)+    {+        vma_delete(allocator, m_DedicatedAllocation.m_ExtraData);+    }+}++void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName)+{+    VMA_ASSERT(pName == VMA_NULL || pName != m_pName);++    FreeName(hAllocator);++    if (pName != VMA_NULL)+        m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName);+}++uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation)+{+    VMA_ASSERT(allocation != VMA_NULL);+    VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);+    VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK);++    if (m_MapCount != 0)+        m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount);++    m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation);+    std::swap(m_BlockAllocation, allocation->m_BlockAllocation);+    m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this);++#if VMA_STATS_STRING_ENABLED+    std::swap(m_BufferImageUsage, allocation->m_BufferImageUsage);+#endif+    return m_MapCount;+}++VmaAllocHandle VmaAllocation_T::GetAllocHandle() const+{+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        return m_BlockAllocation.m_AllocHandle;+    case ALLOCATION_TYPE_DEDICATED:+        return VK_NULL_HANDLE;+    default:+        VMA_ASSERT(0);+        return VK_NULL_HANDLE;+    }+}++VkDeviceSize VmaAllocation_T::GetOffset() const+{+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle);+    case ALLOCATION_TYPE_DEDICATED:+        return 0;+    default:+        VMA_ASSERT(0);+        return 0;+    }+}++VmaPool VmaAllocation_T::GetParentPool() const+{+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        return m_BlockAllocation.m_Block->GetParentPool();+    case ALLOCATION_TYPE_DEDICATED:+        return m_DedicatedAllocation.m_hParentPool;+    default:+        VMA_ASSERT(0);+        return VK_NULL_HANDLE;+    }+}++VkDeviceMemory VmaAllocation_T::GetMemory() const+{+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        return m_BlockAllocation.m_Block->GetDeviceMemory();+    case ALLOCATION_TYPE_DEDICATED:+        return m_DedicatedAllocation.m_hMemory;+    default:+        VMA_ASSERT(0);+        return VK_NULL_HANDLE;+    }+}++void* VmaAllocation_T::GetMappedData() const+{+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        if (m_MapCount != 0 || IsPersistentMap())+        {+            void* pBlockData = m_BlockAllocation.m_Block->GetMappedData();+            VMA_ASSERT(pBlockData != VMA_NULL);+            return (char*)pBlockData + GetOffset();+        }+        else+        {+            return VMA_NULL;+        }+        break;+    case ALLOCATION_TYPE_DEDICATED:+        VMA_ASSERT((m_DedicatedAllocation.m_ExtraData != VMA_NULL && m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL) ==+            (m_MapCount != 0 || IsPersistentMap()));+        return m_DedicatedAllocation.m_ExtraData != VMA_NULL ? m_DedicatedAllocation.m_ExtraData->m_pMappedData : VMA_NULL;+    default:+        VMA_ASSERT(0);+        return VMA_NULL;+    }+}++void VmaAllocation_T::BlockAllocMap()+{+    VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);+    VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");++    if (m_MapCount < 0xFF)+    {+        ++m_MapCount;+    }+    else+    {+        VMA_ASSERT(0 && "Allocation mapped too many times simultaneously.");+    }+}++void VmaAllocation_T::BlockAllocUnmap()+{+    VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK);++    if (m_MapCount > 0)+    {+        --m_MapCount;+    }+    else+    {+        VMA_ASSERT(0 && "Unmapping allocation not previously mapped.");+    }+}++VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData)+{+    VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);+    VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it.");++    EnsureExtraData(hAllocator);++    if (m_MapCount != 0 || IsPersistentMap())+    {+        if (m_MapCount < 0xFF)+        {+            VMA_ASSERT(m_DedicatedAllocation.m_ExtraData->m_pMappedData != VMA_NULL);+            *ppData = m_DedicatedAllocation.m_ExtraData->m_pMappedData;+            ++m_MapCount;+            return VK_SUCCESS;+        }++        VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously.");+        return VK_ERROR_MEMORY_MAP_FAILED;+    }++    VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)(+        hAllocator->m_hDevice,+        m_DedicatedAllocation.m_hMemory,+        0, // offset+        VK_WHOLE_SIZE,+        0, // flags+        ppData);+    if (result == VK_SUCCESS)+    {+        m_DedicatedAllocation.m_ExtraData->m_pMappedData = *ppData;+        m_MapCount = 1;+    }+    return result;+}++void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator)+{+    VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED);++    if (m_MapCount > 0)+    {+        --m_MapCount;+        if (m_MapCount == 0 && !IsPersistentMap())+        {+            VMA_ASSERT(m_DedicatedAllocation.m_ExtraData != VMA_NULL);+            m_DedicatedAllocation.m_ExtraData->m_pMappedData = VMA_NULL;+            (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(+                hAllocator->m_hDevice,+                m_DedicatedAllocation.m_hMemory);+        }+    }+    else+    {+        VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped.");+    }+}++#if VMA_STATS_STRING_ENABLED+void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const+{+    json.WriteString("Type");+    json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]);++    json.WriteString("Size");+    json.WriteNumber(m_Size);+    json.WriteString("Usage");+    json.WriteNumber(m_BufferImageUsage.Value); // It may be uint32_t or uint64_t.++    if (m_pUserData != VMA_NULL)+    {+        json.WriteString("CustomData");+        json.BeginString();+        json.ContinueString_Pointer(m_pUserData);+        json.EndString();+    }+    if (m_pName != VMA_NULL)+    {+        json.WriteString("Name");+        json.WriteString(m_pName);+    }+}+#if VMA_EXTERNAL_MEMORY_WIN32+VkResult VmaAllocation_T::GetWin32Handle(VmaAllocator hAllocator, HANDLE hTargetProcess, HANDLE* pHandle) noexcept+{+    auto pvkGetMemoryWin32HandleKHR = hAllocator->GetVulkanFunctions().vkGetMemoryWin32HandleKHR;+    switch (m_Type)+    {+    case ALLOCATION_TYPE_BLOCK:+        return m_BlockAllocation.m_Block->CreateWin32Handle(hAllocator, pvkGetMemoryWin32HandleKHR, hTargetProcess, pHandle);+    case ALLOCATION_TYPE_DEDICATED:+        EnsureExtraData(hAllocator);+        return m_DedicatedAllocation.m_ExtraData->m_Handle.GetHandle(hAllocator->m_hDevice, m_DedicatedAllocation.m_hMemory, pvkGetMemoryWin32HandleKHR, hTargetProcess, hAllocator->m_UseMutex, pHandle);+    default:+        VMA_ASSERT(0);+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }+}+#endif // VMA_EXTERNAL_MEMORY_WIN32+#endif // VMA_STATS_STRING_ENABLED++void VmaAllocation_T::EnsureExtraData(VmaAllocator hAllocator)+{+    if (m_DedicatedAllocation.m_ExtraData == VMA_NULL)+    {+        m_DedicatedAllocation.m_ExtraData = vma_new(hAllocator, VmaAllocationExtraData)();+    }+}++void VmaAllocation_T::FreeName(VmaAllocator hAllocator)+{+    if(m_pName)+    {+        VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName);+        m_pName = VMA_NULL;+    }+}+#endif // _VMA_ALLOCATION_T_FUNCTIONS++#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS+VmaBlockVector::VmaBlockVector(+    VmaAllocator hAllocator,+    VmaPool hParentPool,+    uint32_t memoryTypeIndex,+    VkDeviceSize preferredBlockSize,+    size_t minBlockCount,+    size_t maxBlockCount,+    VkDeviceSize bufferImageGranularity,+    bool explicitBlockSize,+    uint32_t algorithm,+    float priority,+    VkDeviceSize minAllocationAlignment,+    void* pMemoryAllocateNext)+    : m_hAllocator(hAllocator),+    m_hParentPool(hParentPool),+    m_MemoryTypeIndex(memoryTypeIndex),+    m_PreferredBlockSize(preferredBlockSize),+    m_MinBlockCount(minBlockCount),+    m_MaxBlockCount(maxBlockCount),+    m_BufferImageGranularity(bufferImageGranularity),+    m_ExplicitBlockSize(explicitBlockSize),+    m_Algorithm(algorithm),+    m_Priority(priority),+    m_MinAllocationAlignment(minAllocationAlignment),+    m_pMemoryAllocateNext(pMemoryAllocateNext),+    m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),+    m_NextBlockId(0) {}++VmaBlockVector::~VmaBlockVector()+{+    for (size_t i = m_Blocks.size(); i--; )+    {+        m_Blocks[i]->Destroy(m_hAllocator);+        vma_delete(m_hAllocator, m_Blocks[i]);+    }+}++VkResult VmaBlockVector::CreateMinBlocks()+{+    for (size_t i = 0; i < m_MinBlockCount; ++i)+    {+        VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL);+        if (res != VK_SUCCESS)+        {+            return res;+        }+    }+    return VK_SUCCESS;+}++void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats)+{+    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);++    const size_t blockCount = m_Blocks.size();+    for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)+    {+        const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];+        VMA_ASSERT(pBlock);+        VMA_HEAVY_ASSERT(pBlock->Validate());+        pBlock->m_pMetadata->AddStatistics(inoutStats);+    }+}++void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)+{+    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);++    const size_t blockCount = m_Blocks.size();+    for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)+    {+        const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];+        VMA_ASSERT(pBlock);+        VMA_HEAVY_ASSERT(pBlock->Validate());+        pBlock->m_pMetadata->AddDetailedStatistics(inoutStats);+    }+}++bool VmaBlockVector::IsEmpty()+{+    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);+    return m_Blocks.empty();+}++bool VmaBlockVector::IsCorruptionDetectionEnabled() const+{+    const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;+    return (VMA_DEBUG_DETECT_CORRUPTION != 0) &&+        (VMA_DEBUG_MARGIN > 0) &&+        (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) &&+        (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags;+}++VkResult VmaBlockVector::Allocate(+    VkDeviceSize size,+    VkDeviceSize alignment,+    const VmaAllocationCreateInfo& createInfo,+    VmaSuballocationType suballocType,+    size_t allocationCount,+    VmaAllocation* pAllocations)+{+    size_t allocIndex = 0;+    VkResult res = VK_SUCCESS;++    alignment = VMA_MAX(alignment, m_MinAllocationAlignment);++    if (IsCorruptionDetectionEnabled())+    {+        size = VmaAlignUp<VkDeviceSize>(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));+        alignment = VmaAlignUp<VkDeviceSize>(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE));+    }++    {+        VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);+        for (; allocIndex < allocationCount; ++allocIndex)+        {+            res = AllocatePage(+                size,+                alignment,+                createInfo,+                suballocType,+                pAllocations + allocIndex);+            if (res != VK_SUCCESS)+            {+                break;+            }+        }+    }++    if (res != VK_SUCCESS)+    {+        // Free all already created allocations.+        while (allocIndex--)+            Free(pAllocations[allocIndex]);+        memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);+    }++    return res;+}++VkResult VmaBlockVector::AllocatePage(+    VkDeviceSize size,+    VkDeviceSize alignment,+    const VmaAllocationCreateInfo& createInfo,+    VmaSuballocationType suballocType,+    VmaAllocation* pAllocation)+{+    const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;++    VkDeviceSize freeMemory = 0;+    {+        const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);+        VmaBudget heapBudget = {};+        m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);+        freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0;+    }++    const bool canFallbackToDedicated = !HasExplicitBlockSize() &&+        (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0;+    const bool canCreateNewBlock =+        ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&+        (m_Blocks.size() < m_MaxBlockCount) &&+        (freeMemory >= size || !canFallbackToDedicated);+    uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK;++    // Upper address can only be used with linear allocator and within single memory block.+    if (isUpperAddress &&+        (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1))+    {+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }++    // Early reject: requested allocation size is larger that maximum block size for this block vector.+    if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize)+    {+        return VK_ERROR_OUT_OF_DEVICE_MEMORY;+    }++    // 1. Search existing allocations. Try to allocate.+    if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)+    {+        // Use only last block.+        if (!m_Blocks.empty())+        {+            VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();+            VMA_ASSERT(pCurrBlock);+            VkResult res = AllocateFromBlock(+                pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);+            if (res == VK_SUCCESS)+            {+                VMA_DEBUG_LOG_FORMAT("    Returned from last block #%" PRIu32, pCurrBlock->GetId());+                IncrementallySortBlocks();+                return VK_SUCCESS;+            }+        }+    }+    else+    {+        if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default+        {+            const bool isHostVisible =+                (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0;+            if(isHostVisible)+            {+                const bool isMappingAllowed = (createInfo.flags &+                    (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;+                /*+                For non-mappable allocations, check blocks that are not mapped first.+                For mappable allocations, check blocks that are already mapped first.+                This way, having many blocks, we will separate mappable and non-mappable allocations,+                hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc.+                */+                for(size_t mappingI = 0; mappingI < 2; ++mappingI)+                {+                    // Forward order in m_Blocks - prefer blocks with smallest amount of free space.+                    for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)+                    {+                        VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];+                        VMA_ASSERT(pCurrBlock);+                        const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL;+                        if((mappingI == 0) == (isMappingAllowed == isBlockMapped))+                        {+                            VkResult res = AllocateFromBlock(+                                pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);+                            if (res == VK_SUCCESS)+                            {+                                VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%" PRIu32, pCurrBlock->GetId());+                                IncrementallySortBlocks();+                                return VK_SUCCESS;+                            }+                        }+                    }+                }+            }+            else+            {+                // Forward order in m_Blocks - prefer blocks with smallest amount of free space.+                for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)+                {+                    VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];+                    VMA_ASSERT(pCurrBlock);+                    VkResult res = AllocateFromBlock(+                        pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);+                    if (res == VK_SUCCESS)+                    {+                        VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%" PRIu32, pCurrBlock->GetId());+                        IncrementallySortBlocks();+                        return VK_SUCCESS;+                    }+                }+            }+        }+        else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT+        {+            // Backward order in m_Blocks - prefer blocks with largest amount of free space.+            for (size_t blockIndex = m_Blocks.size(); blockIndex--; )+            {+                VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];+                VMA_ASSERT(pCurrBlock);+                VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);+                if (res == VK_SUCCESS)+                {+                    VMA_DEBUG_LOG_FORMAT("    Returned from existing block #%" PRIu32, pCurrBlock->GetId());+                    IncrementallySortBlocks();+                    return VK_SUCCESS;+                }+            }+        }+    }++    // 2. Try to create new block.+    if (canCreateNewBlock)+    {+        // Calculate optimal size for new block.+        VkDeviceSize newBlockSize = m_PreferredBlockSize;+        uint32_t newBlockSizeShift = 0;+        const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;++        if (!m_ExplicitBlockSize)+        {+            // Allocate 1/8, 1/4, 1/2 as first blocks.+            const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();+            for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)+            {+                const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;+                if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)+                {+                    newBlockSize = smallerNewBlockSize;+                    ++newBlockSizeShift;+                }+                else+                {+                    break;+                }+            }+        }++        size_t newBlockIndex = 0;+        VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?+            CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;+        // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.+        if (!m_ExplicitBlockSize)+        {+            while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)+            {+                const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;+                if (smallerNewBlockSize >= size)+                {+                    newBlockSize = smallerNewBlockSize;+                    ++newBlockSizeShift;+                    res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ?+                        CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY;+                }+                else+                {+                    break;+                }+            }+        }++        if (res == VK_SUCCESS)+        {+            VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];+            VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);++            res = AllocateFromBlock(+                pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation);+            if (res == VK_SUCCESS)+            {+                VMA_DEBUG_LOG_FORMAT("    Created new block #%" PRIu32 " Size=%" PRIu64, pBlock->GetId(), newBlockSize);+                IncrementallySortBlocks();+                return VK_SUCCESS;+            }++            // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.+            return VK_ERROR_OUT_OF_DEVICE_MEMORY;+        }+    }++    return VK_ERROR_OUT_OF_DEVICE_MEMORY;+}++void VmaBlockVector::Free(VmaAllocation hAllocation)+{+    VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL;++    bool budgetExceeded = false;+    {+        const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex);+        VmaBudget heapBudget = {};+        m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1);+        budgetExceeded = heapBudget.usage >= heapBudget.budget;+    }++    // Scope for lock.+    {+        VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);++        VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();++        if (IsCorruptionDetectionEnabled())+        {+            VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize());+            VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value.");+        }++        if (hAllocation->IsPersistentMap())+        {+            pBlock->Unmap(m_hAllocator, 1);+        }++        const bool hadEmptyBlockBeforeFree = HasEmptyBlock();+        pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle());+        pBlock->PostFree(m_hAllocator);+        VMA_HEAVY_ASSERT(pBlock->Validate());++        VMA_DEBUG_LOG_FORMAT("  Freed from MemoryTypeIndex=%" PRIu32, m_MemoryTypeIndex);++        const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount;+        // pBlock became empty after this deallocation.+        if (pBlock->m_pMetadata->IsEmpty())+        {+            // Already had empty block. We don't want to have two, so delete this one.+            if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock)+            {+                pBlockToDelete = pBlock;+                Remove(pBlock);+            }+            // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth.+        }+        // pBlock didn't become empty, but we have another empty block - find and free that one.+        // (This is optional, heuristics.)+        else if (hadEmptyBlockBeforeFree && canDeleteBlock)+        {+            VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();+            if (pLastBlock->m_pMetadata->IsEmpty())+            {+                pBlockToDelete = pLastBlock;+                m_Blocks.pop_back();+            }+        }++        IncrementallySortBlocks();++        m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize());+        hAllocation->Destroy(m_hAllocator);+        m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation);+    }++    // Destruction of a free block. Deferred until this point, outside of mutex+    // lock, for performance reason.+    if (pBlockToDelete != VMA_NULL)+    {+        VMA_DEBUG_LOG_FORMAT("    Deleted empty block #%" PRIu32, pBlockToDelete->GetId());+        pBlockToDelete->Destroy(m_hAllocator);+        vma_delete(m_hAllocator, pBlockToDelete);+    }+}++VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const+{+    VkDeviceSize result = 0;+    for (size_t i = m_Blocks.size(); i--; )+    {+        result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize());+        if (result >= m_PreferredBlockSize)+        {+            break;+        }+    }+    return result;+}++void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)+{+    for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)+    {+        if (m_Blocks[blockIndex] == pBlock)+        {+            VmaVectorRemove(m_Blocks, blockIndex);+            return;+        }+    }+    VMA_ASSERT(0);+}++void VmaBlockVector::IncrementallySortBlocks()+{+    if (!m_IncrementalSort)+        return;+    if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)+    {+        // Bubble sort only until first swap.+        for (size_t i = 1; i < m_Blocks.size(); ++i)+        {+            if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())+            {+                std::swap(m_Blocks[i - 1], m_Blocks[i]);+                return;+            }+        }+    }+}++void VmaBlockVector::SortByFreeSize()+{+    VMA_SORT(m_Blocks.begin(), m_Blocks.end(),+        [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool+        {+            return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize();+        });+}++VkResult VmaBlockVector::AllocateFromBlock(+    VmaDeviceMemoryBlock* pBlock,+    VkDeviceSize size,+    VkDeviceSize alignment,+    VmaAllocationCreateFlags allocFlags,+    void* pUserData,+    VmaSuballocationType suballocType,+    uint32_t strategy,+    VmaAllocation* pAllocation)+{+    const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;++    VmaAllocationRequest currRequest = {};+    if (pBlock->m_pMetadata->CreateAllocationRequest(+        size,+        alignment,+        isUpperAddress,+        suballocType,+        strategy,+        &currRequest))+    {+        return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation);+    }+    return VK_ERROR_OUT_OF_DEVICE_MEMORY;+}++VkResult VmaBlockVector::CommitAllocationRequest(+    VmaAllocationRequest& allocRequest,+    VmaDeviceMemoryBlock* pBlock,+    VkDeviceSize alignment,+    VmaAllocationCreateFlags allocFlags,+    void* pUserData,+    VmaSuballocationType suballocType,+    VmaAllocation* pAllocation)+{+    const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;+    const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;+    const bool isMappingAllowed = (allocFlags &+        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0;++    pBlock->PostAlloc(m_hAllocator);+    // Allocate from pCurrBlock.+    if (mapped)+    {+        VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);+        if (res != VK_SUCCESS)+        {+            return res;+        }+    }++    *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed);+    pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation);+    (*pAllocation)->InitBlockAllocation(+        pBlock,+        allocRequest.allocHandle,+        alignment,+        allocRequest.size, // Not size, as actual allocation size may be larger than requested!+        m_MemoryTypeIndex,+        suballocType,+        mapped);+    VMA_HEAVY_ASSERT(pBlock->Validate());+    if (isUserDataString)+        (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData);+    else+        (*pAllocation)->SetUserData(m_hAllocator, pUserData);+    m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size);+    if (VMA_DEBUG_INITIALIZE_ALLOCATIONS)+    {+        m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);+    }+    if (IsCorruptionDetectionEnabled())+    {+        VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size);+        VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");+    }+    return VK_SUCCESS;+}++VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)+{+    VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };+    allocInfo.pNext = m_pMemoryAllocateNext;+    allocInfo.memoryTypeIndex = m_MemoryTypeIndex;+    allocInfo.allocationSize = blockSize;++#if VMA_BUFFER_DEVICE_ADDRESS+    // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature.+    VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };+    if (m_hAllocator->m_UseKhrBufferDeviceAddress)+    {+        allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;+        VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);+    }+#endif // VMA_BUFFER_DEVICE_ADDRESS++#if VMA_MEMORY_PRIORITY+    VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };+    if (m_hAllocator->m_UseExtMemoryPriority)+    {+        VMA_ASSERT(m_Priority >= 0.F && m_Priority <= 1.F);+        priorityInfo.priority = m_Priority;+        VmaPnextChainPushFront(&allocInfo, &priorityInfo);+    }+#endif // VMA_MEMORY_PRIORITY++#if VMA_EXTERNAL_MEMORY+    // Attach VkExportMemoryAllocateInfoKHR if necessary.+    VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };+    exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex);+    if (exportMemoryAllocInfo.handleTypes != 0)+    {+        VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);+    }+#endif // VMA_EXTERNAL_MEMORY++    VkDeviceMemory mem = VK_NULL_HANDLE;+    VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem);+    if (res < 0)+    {+        return res;+    }++    // New VkDeviceMemory successfully created.++    // Create new Allocation for it.+    VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator);+    pBlock->Init(+        m_hAllocator,+        m_hParentPool,+        m_MemoryTypeIndex,+        mem,+        allocInfo.allocationSize,+        m_NextBlockId++,+        m_Algorithm,+        m_BufferImageGranularity);++    m_Blocks.push_back(pBlock);+    if (pNewBlockIndex != VMA_NULL)+    {+        *pNewBlockIndex = m_Blocks.size() - 1;+    }++    return VK_SUCCESS;+}++bool VmaBlockVector::HasEmptyBlock()+{+    for (size_t index = 0, count = m_Blocks.size(); index < count; ++index)+    {+        VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];+        if (pBlock->m_pMetadata->IsEmpty())+        {+            return true;+        }+    }+    return false;+}++#if VMA_STATS_STRING_ENABLED+void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)+{+    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);+++    json.BeginObject();+    for (size_t i = 0; i < m_Blocks.size(); ++i)+    {+        json.BeginString();+        json.ContinueString(m_Blocks[i]->GetId());+        json.EndString();++        json.BeginObject();+        json.WriteString("MapRefCount");+        json.WriteNumber(m_Blocks[i]->GetMapRefCount());++        m_Blocks[i]->m_pMetadata->PrintDetailedMap(json);+        json.EndObject();+    }+    json.EndObject();+}+#endif // VMA_STATS_STRING_ENABLED++VkResult VmaBlockVector::CheckCorruption()+{+    if (!IsCorruptionDetectionEnabled())+    {+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }++    VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex);+    for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)+    {+        VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];+        VMA_ASSERT(pBlock);+        VkResult res = pBlock->CheckCorruption(m_hAllocator);+        if (res != VK_SUCCESS)+        {+            return res;+        }+    }+    return VK_SUCCESS;+}++#endif // _VMA_BLOCK_VECTOR_FUNCTIONS++#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS+VmaDefragmentationContext_T::VmaDefragmentationContext_T(+    VmaAllocator hAllocator,+    const VmaDefragmentationInfo& info)+    : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass),+    m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass),+    m_BreakCallback(info.pfnBreakCallback),+    m_BreakCallbackUserData(info.pBreakCallbackUserData),+    m_MoveAllocator(hAllocator->GetAllocationCallbacks()),+    m_Moves(m_MoveAllocator),+    m_Algorithm(info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK)+{+    if (info.pool != VMA_NULL)+    {+        m_BlockVectorCount = 1;+        m_PoolBlockVector = &info.pool->m_BlockVector;+        m_pBlockVectors = &m_PoolBlockVector;+        m_PoolBlockVector->SetIncrementalSort(false);+        m_PoolBlockVector->SortByFreeSize();+    }+    else+    {+        m_BlockVectorCount = hAllocator->GetMemoryTypeCount();+        m_PoolBlockVector = VMA_NULL;+        m_pBlockVectors = hAllocator->m_pBlockVectors;+        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)+        {+            VmaBlockVector* vector = m_pBlockVectors[i];+            if (vector != VMA_NULL)+            {+                vector->SetIncrementalSort(false);+                vector->SortByFreeSize();+            }+        }+    }++    switch (m_Algorithm)+    {+    case 0: // Default algorithm+        m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT;+        m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);+        break;+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:+        m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount);+        break;+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:+        if (hAllocator->GetBufferImageGranularity() > 1)+        {+            m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount);+        }+        break;+    default:+        ; // Do nothing.+    }+}++VmaDefragmentationContext_T::~VmaDefragmentationContext_T()+{+    if (m_PoolBlockVector != VMA_NULL)+    {+        m_PoolBlockVector->SetIncrementalSort(true);+    }+    else+    {+        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)+        {+            VmaBlockVector* vector = m_pBlockVectors[i];+            if (vector != VMA_NULL)+                vector->SetIncrementalSort(true);+        }+    }++    if (m_AlgorithmState)+    {+        switch (m_Algorithm)+        {+        case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:+            vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateBalanced*>(m_AlgorithmState), m_BlockVectorCount);+            break;+        case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:+            vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast<StateExtensive*>(m_AlgorithmState), m_BlockVectorCount);+            break;+        default:+            VMA_ASSERT(0);+        }+    }+}++VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo)+{+    if (m_PoolBlockVector != VMA_NULL)+    {+        VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex);++        if (m_PoolBlockVector->GetBlockCount() > 1)+            ComputeDefragmentation(*m_PoolBlockVector, 0);+        else if (m_PoolBlockVector->GetBlockCount() == 1)+            ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0));+    }+    else+    {+        for (uint32_t i = 0; i < m_BlockVectorCount; ++i)+        {+            if (m_pBlockVectors[i] != VMA_NULL)+            {+                VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex);++                if (m_pBlockVectors[i]->GetBlockCount() > 1)+                {+                    if (ComputeDefragmentation(*m_pBlockVectors[i], i))+                        break;+                }+                else if (m_pBlockVectors[i]->GetBlockCount() == 1)+                {+                    if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0)))+                        break;+                }+            }+        }+    }++    moveInfo.moveCount = static_cast<uint32_t>(m_Moves.size());+    if (moveInfo.moveCount > 0)+    {+        moveInfo.pMoves = m_Moves.data();+        return VK_INCOMPLETE;+    }++    moveInfo.pMoves = VMA_NULL;+    return VK_SUCCESS;+}++VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo)+{+    VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true);++    VkResult result = VK_SUCCESS;+    VmaStlAllocator<FragmentedBlock> blockAllocator(m_MoveAllocator.m_pCallbacks);+    VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> immovableBlocks(blockAllocator);+    VmaVector<FragmentedBlock, VmaStlAllocator<FragmentedBlock>> mappedBlocks(blockAllocator);++    VmaAllocator allocator = VMA_NULL;+    for (uint32_t i = 0; i < moveInfo.moveCount; ++i)+    {+        VmaDefragmentationMove& move = moveInfo.pMoves[i];+        size_t prevCount = 0;+        size_t currentCount = 0;+        VkDeviceSize freedBlockSize = 0;++        uint32_t vectorIndex = 0;+        VmaBlockVector* vector = VMA_NULL;+        if (m_PoolBlockVector != VMA_NULL)+        {+            vector = m_PoolBlockVector;+        }+        else+        {+            vectorIndex = move.srcAllocation->GetMemoryTypeIndex();+            vector = m_pBlockVectors[vectorIndex];+            VMA_ASSERT(vector != VMA_NULL);+        }++        switch (move.operation)+        {+        case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY:+        {+            uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation);+            if (mapCount > 0)+            {+                allocator = vector->m_hAllocator;+                VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock();+                bool notPresent = true;+                for (FragmentedBlock& block : mappedBlocks)+                {+                    if (block.block == newMapBlock)+                    {+                        notPresent = false;+                        block.data += mapCount;+                        break;+                    }+                }+                if (notPresent)+                    mappedBlocks.push_back({ mapCount, newMapBlock });+            }++            // Scope for locks, Free have it's own lock+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                prevCount = vector->GetBlockCount();+                freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();+            }+            vector->Free(move.dstTmpAllocation);+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                currentCount = vector->GetBlockCount();+            }++            result = VK_INCOMPLETE;+            break;+        }+        case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE:+        {+            m_PassStats.bytesMoved -= move.srcAllocation->GetSize();+            --m_PassStats.allocationsMoved;+            vector->Free(move.dstTmpAllocation);++            VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock();+            bool notPresent = true;+            for (const FragmentedBlock& block : immovableBlocks)+            {+                if (block.block == newBlock)+                {+                    notPresent = false;+                    break;+                }+            }+            if (notPresent)+                immovableBlocks.push_back({ vectorIndex, newBlock });+            break;+        }+        case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY:+        {+            m_PassStats.bytesMoved -= move.srcAllocation->GetSize();+            --m_PassStats.allocationsMoved;+            // Scope for locks, Free have it's own lock+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                prevCount = vector->GetBlockCount();+                freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize();+            }+            vector->Free(move.srcAllocation);+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                currentCount = vector->GetBlockCount();+            }+            freedBlockSize *= prevCount - currentCount;++            VkDeviceSize dstBlockSize = SIZE_MAX;+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize();+            }+            vector->Free(move.dstTmpAllocation);+            {+                VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);+                freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount());+                currentCount = vector->GetBlockCount();+            }++            result = VK_INCOMPLETE;+            break;+        }+        default:+            VMA_ASSERT(0);+        }++        if (prevCount > currentCount)+        {+            size_t freedBlocks = prevCount - currentCount;+            m_PassStats.deviceMemoryBlocksFreed += static_cast<uint32_t>(freedBlocks);+            m_PassStats.bytesFreed += freedBlockSize;+        }++        if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT &&+            m_AlgorithmState != VMA_NULL)+        {+            // Avoid unnecessary tries to allocate when new free block is available+            StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[vectorIndex];+            if (state.firstFreeBlock != SIZE_MAX)+            {+                const size_t diff = prevCount - currentCount;+                if (state.firstFreeBlock >= diff)+                {+                    state.firstFreeBlock -= diff;+                    if (state.firstFreeBlock != 0)+                        state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty();+                }+                else+                    state.firstFreeBlock = 0;+            }+        }+    }+    moveInfo.moveCount = 0;+    moveInfo.pMoves = VMA_NULL;+    m_Moves.clear();++    // Update stats+    m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved;+    m_GlobalStats.bytesFreed += m_PassStats.bytesFreed;+    m_GlobalStats.bytesMoved += m_PassStats.bytesMoved;+    m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed;+    m_PassStats = { 0 };++    // Move blocks with immovable allocations according to algorithm+    if (!immovableBlocks.empty())+    {+        do+        {+            if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT)+            {+                if (m_AlgorithmState != VMA_NULL)+                {+                    bool swapped = false;+                    // Move to the start of free blocks range+                    for (const FragmentedBlock& block : immovableBlocks)+                    {+                        StateExtensive& state = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[block.data];+                        if (state.operation != StateExtensive::Operation::Cleanup)+                        {+                            VmaBlockVector* vector = m_pBlockVectors[block.data];+                            VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);++                            for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i)+                            {+                                if (vector->GetBlock(i) == block.block)+                                {+                                    std::swap(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]);+                                    if (state.firstFreeBlock != SIZE_MAX)+                                    {+                                        if (i + 1 < state.firstFreeBlock)+                                        {+                                            if (state.firstFreeBlock > 1)+                                                std::swap(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]);+                                            else+                                                --state.firstFreeBlock;+                                        }+                                    }+                                    swapped = true;+                                    break;+                                }+                            }+                        }+                    }+                    if (swapped)+                        result = VK_INCOMPLETE;+                    break;+                }+            }++            // Move to the beginning+            for (const FragmentedBlock& block : immovableBlocks)+            {+                VmaBlockVector* vector = m_pBlockVectors[block.data];+                VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex);++                for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i)+                {+                    if (vector->GetBlock(i) == block.block)+                    {+                        std::swap(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]);+                        break;+                    }+                }+            }+        } while (false);+    }++    // Bulk-map destination blocks+    for (const FragmentedBlock& block : mappedBlocks)+    {+        VkResult res = block.block->Map(allocator, block.data, VMA_NULL);+        VMA_ASSERT(res == VK_SUCCESS);+    }+    return result;+}++bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index)+{+    switch (m_Algorithm)+    {+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT:+        return ComputeDefragmentation_Fast(vector);+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT:+        return ComputeDefragmentation_Balanced(vector, index, true);+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT:+        return ComputeDefragmentation_Full(vector);+    case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT:+        return ComputeDefragmentation_Extensive(vector, index);+    default:+        VMA_ASSERT(0);+        return ComputeDefragmentation_Balanced(vector, index, true);+    }+}++VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData(+    VmaAllocHandle handle, VmaBlockMetadata* metadata)+{+    MoveAllocationData moveData;+    moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle);+    moveData.size = moveData.move.srcAllocation->GetSize();+    moveData.alignment = moveData.move.srcAllocation->GetAlignment();+    moveData.type = moveData.move.srcAllocation->GetSuballocationType();+    moveData.flags = 0;++    if (moveData.move.srcAllocation->IsPersistentMap())+        moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT;+    if (moveData.move.srcAllocation->IsMappingAllowed())+        moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;++    return moveData;+}++VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes)+{+    // Check custom criteria if exists+    if (m_BreakCallback && m_BreakCallback(m_BreakCallbackUserData))+        return CounterStatus::End;++    // Ignore allocation if will exceed max size for copy+    if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes)+    {+        if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE)+            return CounterStatus::Ignore;+        return CounterStatus::End;+    }++    m_IgnoredAllocs = 0;+    return CounterStatus::Pass;+}++bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes)+{+    m_PassStats.bytesMoved += bytes;+    // Early return when max found+    if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes)+    {+        VMA_ASSERT((m_PassStats.allocationsMoved == m_MaxPassAllocations ||+            m_PassStats.bytesMoved == m_MaxPassBytes) && "Exceeded maximal pass threshold!");+        return true;+    }+    return false;+}++bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block)+{+    VmaBlockMetadata* metadata = block->m_pMetadata;++    for (VmaAllocHandle handle = metadata->GetAllocationListBegin();+        handle != VK_NULL_HANDLE;+        handle = metadata->GetNextAllocation(handle))+    {+        MoveAllocationData moveData = GetMoveData(handle, metadata);+        // Ignore newly created allocations by defragmentation algorithm+        if (moveData.move.srcAllocation->GetUserData() == this)+            continue;+        switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+        {+        case CounterStatus::Ignore:+            continue;+        case CounterStatus::End:+            return true;+        case CounterStatus::Pass:+            break;+        default:+            VMA_ASSERT(0);+        }++        VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();+        if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size)+        {+            VmaAllocationRequest request = {};+            if (metadata->CreateAllocationRequest(+                moveData.size,+                moveData.alignment,+                false,+                moveData.type,+                VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,+                &request))+            {+                if (metadata->GetAllocationOffset(request.allocHandle) < offset)+                {+                    if (vector.CommitAllocationRequest(+                        request,+                        block,+                        moveData.alignment,+                        moveData.flags,+                        this,+                        moveData.type,+                        &moveData.move.dstTmpAllocation) == VK_SUCCESS)+                    {+                        m_Moves.push_back(moveData.move);+                        if (IncrementCounters(moveData.size))+                            return true;+                    }+                }+            }+        }+    }+    return false;+}++bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector)+{+    for (; start < end; ++start)+    {+        VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start);+        if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size)+        {+            if (vector.AllocateFromBlock(dstBlock,+                data.size,+                data.alignment,+                data.flags,+                this,+                data.type,+                0,+                &data.move.dstTmpAllocation) == VK_SUCCESS)+            {+                m_Moves.push_back(data.move);+                if (IncrementCounters(data.size))+                    return true;+                break;+            }+        }+    }+    return false;+}++bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector)+{+    // Move only between blocks++    // Go through allocations in last blocks and try to fit them inside first ones+    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)+    {+        VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;++        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();+            handle != VK_NULL_HANDLE;+            handle = metadata->GetNextAllocation(handle))+        {+            MoveAllocationData moveData = GetMoveData(handle, metadata);+            // Ignore newly created allocations by defragmentation algorithm+            if (moveData.move.srcAllocation->GetUserData() == this)+                continue;+            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+            {+            case CounterStatus::Ignore:+                continue;+            case CounterStatus::End:+                return true;+            case CounterStatus::Pass:+                break;+            default:+                VMA_ASSERT(0);+            }++            // Check all previous blocks for free space+            if (AllocInOtherBlock(0, i, moveData, vector))+                return true;+        }+    }+    return false;+}++bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update)+{+    // Go over every allocation and try to fit it in previous blocks at lowest offsets,+    // if not possible: realloc within single block to minimize offset (exclude offset == 0),+    // but only if there are noticeable gaps between them (some heuristic, ex. average size of allocation in block)+    VMA_ASSERT(m_AlgorithmState != VMA_NULL);++    StateBalanced& vectorState = reinterpret_cast<StateBalanced*>(m_AlgorithmState)[index];+    if (update && vectorState.avgAllocSize == UINT64_MAX)+        UpdateVectorStatistics(vector, vectorState);++    const size_t startMoveCount = m_Moves.size();+    VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2;+    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)+    {+        VmaDeviceMemoryBlock* block = vector.GetBlock(i);+        VmaBlockMetadata* metadata = block->m_pMetadata;+        VkDeviceSize prevFreeRegionSize = 0;++        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();+            handle != VK_NULL_HANDLE;+            handle = metadata->GetNextAllocation(handle))+        {+            MoveAllocationData moveData = GetMoveData(handle, metadata);+            // Ignore newly created allocations by defragmentation algorithm+            if (moveData.move.srcAllocation->GetUserData() == this)+                continue;+            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+            {+            case CounterStatus::Ignore:+                continue;+            case CounterStatus::End:+                return true;+            case CounterStatus::Pass:+                break;+            default:+                VMA_ASSERT(0);+            }++            // Check all previous blocks for free space+            const size_t prevMoveCount = m_Moves.size();+            if (AllocInOtherBlock(0, i, moveData, vector))+                return true;++            VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle);+            // If no room found then realloc within block for lower offset+            VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();+            if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)+            {+                // Check if realloc will make sense+                if (prevFreeRegionSize >= minimalFreeRegion ||+                    nextFreeRegionSize >= minimalFreeRegion ||+                    moveData.size <= vectorState.avgFreeSize ||+                    moveData.size <= vectorState.avgAllocSize)+                {+                    VmaAllocationRequest request = {};+                    if (metadata->CreateAllocationRequest(+                        moveData.size,+                        moveData.alignment,+                        false,+                        moveData.type,+                        VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,+                        &request))+                    {+                        if (metadata->GetAllocationOffset(request.allocHandle) < offset)+                        {+                            if (vector.CommitAllocationRequest(+                                request,+                                block,+                                moveData.alignment,+                                moveData.flags,+                                this,+                                moveData.type,+                                &moveData.move.dstTmpAllocation) == VK_SUCCESS)+                            {+                                m_Moves.push_back(moveData.move);+                                if (IncrementCounters(moveData.size))+                                    return true;+                            }+                        }+                    }+                }+            }+            prevFreeRegionSize = nextFreeRegionSize;+        }+    }++    // No moves performed, update statistics to current vector state+    if (startMoveCount == m_Moves.size() && !update)+    {+        vectorState.avgAllocSize = UINT64_MAX;+        return ComputeDefragmentation_Balanced(vector, index, false);+    }+    return false;+}++bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector)+{+    // Go over every allocation and try to fit it in previous blocks at lowest offsets,+    // if not possible: realloc within single block to minimize offset (exclude offset == 0)++    for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i)+    {+        VmaDeviceMemoryBlock* block = vector.GetBlock(i);+        VmaBlockMetadata* metadata = block->m_pMetadata;++        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();+            handle != VK_NULL_HANDLE;+            handle = metadata->GetNextAllocation(handle))+        {+            MoveAllocationData moveData = GetMoveData(handle, metadata);+            // Ignore newly created allocations by defragmentation algorithm+            if (moveData.move.srcAllocation->GetUserData() == this)+                continue;+            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+            {+            case CounterStatus::Ignore:+                continue;+            case CounterStatus::End:+                return true;+            case CounterStatus::Pass:+                break;+            default:+                VMA_ASSERT(0);+            }++            // Check all previous blocks for free space+            const size_t prevMoveCount = m_Moves.size();+            if (AllocInOtherBlock(0, i, moveData, vector))+                return true;++            // If no room found then realloc within block for lower offset+            VkDeviceSize offset = moveData.move.srcAllocation->GetOffset();+            if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size)+            {+                VmaAllocationRequest request = {};+                if (metadata->CreateAllocationRequest(+                    moveData.size,+                    moveData.alignment,+                    false,+                    moveData.type,+                    VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT,+                    &request))+                {+                    if (metadata->GetAllocationOffset(request.allocHandle) < offset)+                    {+                        if (vector.CommitAllocationRequest(+                            request,+                            block,+                            moveData.alignment,+                            moveData.flags,+                            this,+                            moveData.type,+                            &moveData.move.dstTmpAllocation) == VK_SUCCESS)+                        {+                            m_Moves.push_back(moveData.move);+                            if (IncrementCounters(moveData.size))+                                return true;+                        }+                    }+                }+            }+        }+    }+    return false;+}++bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index)+{+    // First free single block, then populate it to the brim, then free another block, and so on++    // Fallback to previous algorithm since without granularity conflicts it can achieve max packing+    if (vector.m_BufferImageGranularity == 1)+        return ComputeDefragmentation_Full(vector);++    VMA_ASSERT(m_AlgorithmState != VMA_NULL);++    StateExtensive& vectorState = reinterpret_cast<StateExtensive*>(m_AlgorithmState)[index];++    bool texturePresent = false;+    bool bufferPresent = false;+    bool otherPresent = false;+    switch (vectorState.operation)+    {+    case StateExtensive::Operation::Done: // Vector defragmented+        return false;+    case StateExtensive::Operation::FindFreeBlockBuffer:+    case StateExtensive::Operation::FindFreeBlockTexture:+    case StateExtensive::Operation::FindFreeBlockAll:+    {+        // No more blocks to free, just perform fast realloc and move to cleanup+        if (vectorState.firstFreeBlock == 0)+        {+            vectorState.operation = StateExtensive::Operation::Cleanup;+            return ComputeDefragmentation_Fast(vector);+        }++        // No free blocks, have to clear last one+        size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1;+        VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata;++        const size_t prevMoveCount = m_Moves.size();+        for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin();+            handle != VK_NULL_HANDLE;+            handle = freeMetadata->GetNextAllocation(handle))+        {+            MoveAllocationData moveData = GetMoveData(handle, freeMetadata);+            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+            {+            case CounterStatus::Ignore:+                continue;+            case CounterStatus::End:+                return true;+            case CounterStatus::Pass:+                break;+            default:+                VMA_ASSERT(0);+            }++            // Check all previous blocks for free space+            if (AllocInOtherBlock(0, last, moveData, vector))+            {+                // Full clear performed already+                if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE)+                    vectorState.firstFreeBlock = last;+                return true;+            }+        }++        if (prevMoveCount == m_Moves.size())+        {+            // Cannot perform full clear, have to move data in other blocks around+            if (last != 0)+            {+                for (size_t i = last - 1; i; --i)+                {+                    if (ReallocWithinBlock(vector, vector.GetBlock(i)))+                        return true;+                }+            }++            if (prevMoveCount == m_Moves.size())+            {+                // No possible reallocs within blocks, try to move them around fast+                return ComputeDefragmentation_Fast(vector);+            }+        }+        else+        {+            switch (vectorState.operation)+            {+            case StateExtensive::Operation::FindFreeBlockBuffer:+                vectorState.operation = StateExtensive::Operation::MoveBuffers;+                break;+            case StateExtensive::Operation::FindFreeBlockTexture:+                vectorState.operation = StateExtensive::Operation::MoveTextures;+                break;+            case StateExtensive::Operation::FindFreeBlockAll:+                vectorState.operation = StateExtensive::Operation::MoveAll;+                break;+            default:+                VMA_ASSERT(0);+                vectorState.operation = StateExtensive::Operation::MoveTextures;+            }+            vectorState.firstFreeBlock = last;+            // Nothing done, block found without reallocations, can perform another reallocs in same pass+            return ComputeDefragmentation_Extensive(vector, index);+        }+        break;+    }+    case StateExtensive::Operation::MoveTextures:+    {+        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector,+            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))+        {+            if (texturePresent)+            {+                vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture;+                return ComputeDefragmentation_Extensive(vector, index);+            }++            if (!bufferPresent && !otherPresent)+            {+                vectorState.operation = StateExtensive::Operation::Cleanup;+                break;+            }++            // No more textures to move, check buffers+            vectorState.operation = StateExtensive::Operation::MoveBuffers;+            bufferPresent = false;+            otherPresent = false;+        }+        else+            break;+        VMA_FALLTHROUGH; // Fallthrough+    }+    case StateExtensive::Operation::MoveBuffers:+    {+        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector,+            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))+        {+            if (bufferPresent)+            {+                vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;+                return ComputeDefragmentation_Extensive(vector, index);+            }++            if (!otherPresent)+            {+                vectorState.operation = StateExtensive::Operation::Cleanup;+                break;+            }++            // No more buffers to move, check all others+            vectorState.operation = StateExtensive::Operation::MoveAll;+            otherPresent = false;+        }+        else+            break;+        VMA_FALLTHROUGH; // Fallthrough+    }+    case StateExtensive::Operation::MoveAll:+    {+        if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector,+            vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent))+        {+            if (otherPresent)+            {+                vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer;+                return ComputeDefragmentation_Extensive(vector, index);+            }+            // Everything moved+            vectorState.operation = StateExtensive::Operation::Cleanup;+        }+        break;+    }+    case StateExtensive::Operation::Cleanup:+        // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062).+        break;+    }++    if (vectorState.operation == StateExtensive::Operation::Cleanup)+    {+        // All other work done, pack data in blocks even tighter if possible+        const size_t prevMoveCount = m_Moves.size();+        for (size_t i = 0; i < vector.GetBlockCount(); ++i)+        {+            if (ReallocWithinBlock(vector, vector.GetBlock(i)))+                return true;+        }++        if (prevMoveCount == m_Moves.size())+            vectorState.operation = StateExtensive::Operation::Done;+    }+    return false;+}++void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state)+{+    size_t allocCount = 0;+    size_t freeCount = 0;+    state.avgFreeSize = 0;+    state.avgAllocSize = 0;++    for (size_t i = 0; i < vector.GetBlockCount(); ++i)+    {+        VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata;++        allocCount += metadata->GetAllocationCount();+        freeCount += metadata->GetFreeRegionsCount();+        state.avgFreeSize += metadata->GetSumFreeSize();+        state.avgAllocSize += metadata->GetSize();+    }++    state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount;+    state.avgFreeSize /= freeCount;+}++bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType,+    VmaBlockVector& vector, size_t firstFreeBlock,+    bool& texturePresent, bool& bufferPresent, bool& otherPresent)+{+    const size_t prevMoveCount = m_Moves.size();+    for (size_t i = firstFreeBlock ; i;)+    {+        VmaDeviceMemoryBlock* block = vector.GetBlock(--i);+        VmaBlockMetadata* metadata = block->m_pMetadata;++        for (VmaAllocHandle handle = metadata->GetAllocationListBegin();+            handle != VK_NULL_HANDLE;+            handle = metadata->GetNextAllocation(handle))+        {+            MoveAllocationData moveData = GetMoveData(handle, metadata);+            // Ignore newly created allocations by defragmentation algorithm+            if (moveData.move.srcAllocation->GetUserData() == this)+                continue;+            switch (CheckCounters(moveData.move.srcAllocation->GetSize()))+            {+            case CounterStatus::Ignore:+                continue;+            case CounterStatus::End:+                return true;+            case CounterStatus::Pass:+                break;+            default:+                VMA_ASSERT(0);+            }++            // Move only single type of resources at once+            if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType))+            {+                // Try to fit allocation into free blocks+                if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector))+                    return false;+            }++            if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL))+                texturePresent = true;+            else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER))+                bufferPresent = true;+            else+                otherPresent = true;+        }+    }+    return prevMoveCount == m_Moves.size();+}+#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS++#ifndef _VMA_POOL_T_FUNCTIONS+VmaPool_T::VmaPool_T(+    VmaAllocator hAllocator,+    const VmaPoolCreateInfo& createInfo,+    VkDeviceSize preferredBlockSize)+    : m_BlockVector(+        hAllocator,+        this, // hParentPool+        createInfo.memoryTypeIndex,+        createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,+        createInfo.minBlockCount,+        createInfo.maxBlockCount,+        (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),+        createInfo.blockSize != 0, // explicitBlockSize+        createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm+        createInfo.priority,+        VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),+        createInfo.pMemoryAllocateNext),+    m_Id(0),+    m_Name(VMA_NULL) {}++VmaPool_T::~VmaPool_T()+{+    VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);++    const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();+    VmaFreeString(allocs, m_Name);+}++void VmaPool_T::SetName(const char* pName)+{+    const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();+    VmaFreeString(allocs, m_Name);++    if (pName != VMA_NULL)+    {+        m_Name = VmaCreateStringCopy(allocs, pName);+    }+    else+    {+        m_Name = VMA_NULL;+    }+}+#endif // _VMA_POOL_T_FUNCTIONS++#ifndef _VMA_ALLOCATOR_T_FUNCTIONS+VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :+    m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0),+    m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0),+    m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0),+    m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0),+    m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0),+    m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0),+    m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0),+    m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0),+    m_UseKhrMaintenance4((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0),+    m_UseKhrMaintenance5((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT) != 0),+    m_UseKhrExternalMemoryWin32((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0),+    m_hDevice(pCreateInfo->device),+    m_hInstance(pCreateInfo->instance),+    m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),+    m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?+        *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),+    m_AllocationObjectAllocator(&m_AllocationCallbacks),+    m_HeapSizeLimitMask(0),+    m_DeviceMemoryCount(0),+    m_PreferredLargeHeapBlockSize(0),+    m_PhysicalDevice(pCreateInfo->physicalDevice),+    m_GpuDefragmentationMemoryTypeBits(UINT32_MAX),+    m_NextPoolId(0),+    m_GlobalMemoryTypeBits(UINT32_MAX)+{+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        m_UseKhrDedicatedAllocation = false;+        m_UseKhrBindMemory2 = false;+    }++    if(VMA_DEBUG_DETECT_CORRUPTION)+    {+        // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it.+        VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0);+    }++    VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance);++    if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0))+    {+#if !(VMA_DEDICATED_ALLOCATION)+        if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)+        {+            VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros.");+        }+#endif+#if !(VMA_BIND_MEMORY2)+        if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)+        {+            VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros.");+        }+#endif+    }+#if !(VMA_MEMORY_BUDGET)+    if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros.");+    }+#endif+#if !(VMA_BUFFER_DEVICE_ADDRESS)+    if(m_UseKhrBufferDeviceAddress)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif+#if VMA_VULKAN_VERSION < 1004000+    VMA_ASSERT(m_VulkanApiVersion < VK_MAKE_VERSION(1, 4, 0) && "vulkanApiVersion >= VK_API_VERSION_1_4 but required Vulkan version is disabled by preprocessor macros.");+#endif+#if VMA_VULKAN_VERSION < 1003000+    VMA_ASSERT(m_VulkanApiVersion < VK_MAKE_VERSION(1, 3, 0) && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros.");+#endif+#if VMA_VULKAN_VERSION < 1002000+    VMA_ASSERT(m_VulkanApiVersion < VK_MAKE_VERSION(1, 2, 0) && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros.");+#endif+#if VMA_VULKAN_VERSION < 1001000+    VMA_ASSERT(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0) && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros.");+#endif+#if !(VMA_MEMORY_PRIORITY)+    if(m_UseExtMemoryPriority)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif+#if !(VMA_KHR_MAINTENANCE4)+    if(m_UseKhrMaintenance4)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif+#if !(VMA_KHR_MAINTENANCE5)+    if(m_UseKhrMaintenance5)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif+#if !(VMA_KHR_MAINTENANCE5)+    if(m_UseKhrMaintenance5)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif++#if !(VMA_EXTERNAL_MEMORY_WIN32)+    if(m_UseKhrExternalMemoryWin32)+    {+        VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro.");+    }+#endif++    memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));+    memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));+    memset(&m_MemProps, 0, sizeof(m_MemProps));++    memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));+    memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions));++#if VMA_EXTERNAL_MEMORY+    memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes));+#endif // #if VMA_EXTERNAL_MEMORY++    if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)+    {+        m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData;+        m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;+        m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;+    }++    ImportVulkanFunctions(pCreateInfo->pVulkanFunctions);++    (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties);+    (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps);++    VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT));+    VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY));+    VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity));+    VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize));++    m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?+        pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);++    m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits();++#if VMA_EXTERNAL_MEMORY+    if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL)+    {+        memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes,+            sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount());+    }+#endif // #if VMA_EXTERNAL_MEMORY++    if(pCreateInfo->pHeapSizeLimit != VMA_NULL)+    {+        for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)+        {+            const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex];+            if(limit != VK_WHOLE_SIZE)+            {+                m_HeapSizeLimitMask |= 1U << heapIndex;+                if(limit < m_MemProps.memoryHeaps[heapIndex].size)+                {+                    m_MemProps.memoryHeaps[heapIndex].size = limit;+                }+            }+        }+    }++    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+    {+        // Create only supported types+        if((m_GlobalMemoryTypeBits & (1U << memTypeIndex)) != 0)+        {+            const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex);+            m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)(+                this,+                VK_NULL_HANDLE, // hParentPool+                memTypeIndex,+                preferredBlockSize,+                0,+                SIZE_MAX,+                GetBufferImageGranularity(),+                false, // explicitBlockSize+                0, // algorithm+                0.5F, // priority (0.5 is the default per Vulkan spec)+                GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment+                VMA_NULL); // // pMemoryAllocateNext+            // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here,+            // because minBlockCount is 0.+        }+    }+}++VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo)+{+    VkResult res = VK_SUCCESS;++#if VMA_MEMORY_BUDGET+    if(m_UseExtMemoryBudget)+    {+        UpdateVulkanBudget();+    }+#endif // #if VMA_MEMORY_BUDGET++    return res;+}++VmaAllocator_T::~VmaAllocator_T()+{+    VMA_ASSERT(m_Pools.IsEmpty());++    for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )+    {+        vma_delete(this, m_pBlockVectors[memTypeIndex]);+    }+}++void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions)+{+#if VMA_STATIC_VULKAN_FUNCTIONS == 1+    ImportVulkanFunctions_Static();+#endif++    if(pVulkanFunctions != VMA_NULL)+    {+        ImportVulkanFunctions_Custom(pVulkanFunctions);+    }++#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1+    ImportVulkanFunctions_Dynamic();+#endif++    ValidateVulkanFunctions();+}++#if VMA_STATIC_VULKAN_FUNCTIONS == 1++void VmaAllocator_T::ImportVulkanFunctions_Static()+{+    // Vulkan 1.0+    m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr;+    m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr;+    m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties;+    m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties;+    m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;+    m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory;+    m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory;+    m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory;+    m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges;+    m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges;+    m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory;+    m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory;+    m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements;+    m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements;+    m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer;+    m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer;+    m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage;+    m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage;+    m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer;++    // Vulkan 1.1+#if VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2;+        m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2;+        m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2;+        m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2;+    }+#endif++#if VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2;+    }+#endif++#if VMA_VULKAN_VERSION >= 1003000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))+    {+        m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements;+        m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements;+    }+#endif+}++#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1++void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions)+{+    VMA_ASSERT(pVulkanFunctions != VMA_NULL);++#define VMA_COPY_IF_NOT_NULL(funcName) \+    if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName;++    VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr);+    VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr);+    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties);+    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties);+    VMA_COPY_IF_NOT_NULL(vkAllocateMemory);+    VMA_COPY_IF_NOT_NULL(vkFreeMemory);+    VMA_COPY_IF_NOT_NULL(vkMapMemory);+    VMA_COPY_IF_NOT_NULL(vkUnmapMemory);+    VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges);+    VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges);+    VMA_COPY_IF_NOT_NULL(vkBindBufferMemory);+    VMA_COPY_IF_NOT_NULL(vkBindImageMemory);+    VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements);+    VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements);+    VMA_COPY_IF_NOT_NULL(vkCreateBuffer);+    VMA_COPY_IF_NOT_NULL(vkDestroyBuffer);+    VMA_COPY_IF_NOT_NULL(vkCreateImage);+    VMA_COPY_IF_NOT_NULL(vkDestroyImage);+    VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer);++#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR);+    VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR);+#endif++#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000+    VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR);+    VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR);+#endif++#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000+    VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR);+#endif++#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements);+    VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements);+#endif+#if VMA_EXTERNAL_MEMORY_WIN32+    VMA_COPY_IF_NOT_NULL(vkGetMemoryWin32HandleKHR);+#endif+#undef VMA_COPY_IF_NOT_NULL+}++#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1++void VmaAllocator_T::ImportVulkanFunctions_Dynamic()+{+    VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr &&+        "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass "+        "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. "+        "Other members can be null.");++#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \+    if(m_VulkanFunctions.memberName == VMA_NULL) \+        m_VulkanFunctions.memberName = \+            (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString);+#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \+    if(m_VulkanFunctions.memberName == VMA_NULL) \+        m_VulkanFunctions.memberName = \+            (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString);++    VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties");+    VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties");+    VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory");+    VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory");+    VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory");+    VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory");+    VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges");+    VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges");+    VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory");+    VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory");+    VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements");+    VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements");+    VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer");+    VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer");+    VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage");+    VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage");+    VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer");++#if VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2");+        VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2");+        VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2");+        VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2");+    }+#endif++#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");+        // Try to fetch the pointer from the other name, based on suspected driver bug - see issue #410.+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");+    }+    else if(m_UseExtMemoryBudget)+    {+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");+        // Try to fetch the pointer from the other name, based on suspected driver bug - see issue #410.+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");+    }+#endif++#if VMA_DEDICATED_ALLOCATION+    if(m_UseKhrDedicatedAllocation)+    {+        VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR");+        VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR");+    }+#endif++#if VMA_BIND_MEMORY2+    if(m_UseKhrBindMemory2)+    {+        VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR");+        VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR");+    }+#endif // #if VMA_BIND_MEMORY2++#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2");+    }+    else if(m_UseExtMemoryBudget)+    {+        VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR");+    }+#endif // #if VMA_MEMORY_BUDGET++#if VMA_VULKAN_VERSION >= 1003000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))+    {+        VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements");+        VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements");+    }+#endif+#if VMA_KHR_MAINTENANCE4+    if(m_UseKhrMaintenance4)+    {+        VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirementsKHR, "vkGetDeviceBufferMemoryRequirementsKHR");+        VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirementsKHR, "vkGetDeviceImageMemoryRequirementsKHR");+    }+#endif+#if VMA_EXTERNAL_MEMORY_WIN32+    if (m_UseKhrExternalMemoryWin32)+    {+        VMA_FETCH_DEVICE_FUNC(vkGetMemoryWin32HandleKHR, PFN_vkGetMemoryWin32HandleKHR, "vkGetMemoryWin32HandleKHR");+    }+#endif+#undef VMA_FETCH_DEVICE_FUNC+#undef VMA_FETCH_INSTANCE_FUNC+}++#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1++void VmaAllocator_T::ValidateVulkanFunctions() const+{+    VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL);+    VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL);++#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation)+    {+        VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL);+        VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL);+    }+#endif++#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000+    if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2)+    {+        VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL);+        VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL);+    }+#endif++#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000+    if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL);+    }+#endif+#if VMA_EXTERNAL_MEMORY_WIN32+    if (m_UseKhrExternalMemoryWin32)+    {+        VMA_ASSERT(m_VulkanFunctions.vkGetMemoryWin32HandleKHR != VMA_NULL);+    }+#endif++    // Not validating these due to suspected driver bugs with these function+    // pointers being null despite correct extension or Vulkan version is enabled.+    // See issue #397. Their usage in VMA is optional anyway.+    //+    // VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL);+    // VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL);+}++VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex)+{+    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);+    const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;+    const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE;+    return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32);+}++VkResult VmaAllocator_T::AllocateMemoryOfType(+    VmaPool pool,+    VkDeviceSize size,+    VkDeviceSize alignment,+    bool dedicatedPreferred,+    VkBuffer dedicatedBuffer,+    VkImage dedicatedImage,+    VmaBufferImageUsage dedicatedBufferImageUsage,+    const VmaAllocationCreateInfo& createInfo,+    uint32_t memTypeIndex,+    VmaSuballocationType suballocType,+    VmaDedicatedAllocationList& dedicatedAllocations,+    VmaBlockVector& blockVector,+    size_t allocationCount,+    VmaAllocation* pAllocations)+{+    VMA_ASSERT(pAllocations != VMA_NULL);+    VMA_DEBUG_LOG_FORMAT("  AllocateMemory: MemoryTypeIndex=%" PRIu32 ", AllocationCount=%zu, Size=%" PRIu64, memTypeIndex, allocationCount, size);++    VmaAllocationCreateInfo finalCreateInfo = createInfo;+    VkResult res = CalcMemTypeParams(+        finalCreateInfo,+        memTypeIndex,+        size,+        allocationCount);+    if(res != VK_SUCCESS)+        return res;++    if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)+    {+        return AllocateDedicatedMemory(+            pool,+            size,+            suballocType,+            dedicatedAllocations,+            memTypeIndex,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,+            (finalCreateInfo.flags &+                (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,+            finalCreateInfo.pUserData,+            finalCreateInfo.priority,+            dedicatedBuffer,+            dedicatedImage,+            dedicatedBufferImageUsage,+            allocationCount,+            pAllocations,+            blockVector.GetAllocationNextPtr());+    }++    const bool canAllocateDedicated =+        (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&+        (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize());++    if(canAllocateDedicated)+    {+        // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size.+        if(size > blockVector.GetPreferredBlockSize() / 2)+        {+            dedicatedPreferred = true;+        }+        // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget,+        // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above+        // 3/4 of the maximum allocation count.+        if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 &&+            m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4)+        {+            dedicatedPreferred = false;+        }++        if(dedicatedPreferred)+        {+            res = AllocateDedicatedMemory(+                pool,+                size,+                suballocType,+                dedicatedAllocations,+                memTypeIndex,+                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,+                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,+                (finalCreateInfo.flags &+                    (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,+                (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,+                finalCreateInfo.pUserData,+                finalCreateInfo.priority,+                dedicatedBuffer,+                dedicatedImage,+                dedicatedBufferImageUsage,+                allocationCount,+                pAllocations,+                blockVector.GetAllocationNextPtr());+            if(res == VK_SUCCESS)+            {+                // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.+                VMA_DEBUG_LOG("    Allocated as DedicatedMemory");+                return VK_SUCCESS;+            }+        }+    }++    res = blockVector.Allocate(+        size,+        alignment,+        finalCreateInfo,+        suballocType,+        allocationCount,+        pAllocations);+    if(res == VK_SUCCESS)+        return VK_SUCCESS;++    // Try dedicated memory.+    if(canAllocateDedicated && !dedicatedPreferred)+    {+        res = AllocateDedicatedMemory(+            pool,+            size,+            suballocType,+            dedicatedAllocations,+            memTypeIndex,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0,+            (finalCreateInfo.flags &+                (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0,+            (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0,+            finalCreateInfo.pUserData,+            finalCreateInfo.priority,+            dedicatedBuffer,+            dedicatedImage,+            dedicatedBufferImageUsage,+            allocationCount,+            pAllocations,+            blockVector.GetAllocationNextPtr());+        if(res == VK_SUCCESS)+        {+            // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here.+            VMA_DEBUG_LOG("    Allocated as DedicatedMemory");+            return VK_SUCCESS;+        }+    }+    // Everything failed: Return error code.+    VMA_DEBUG_LOG("    vkAllocateMemory FAILED");+    return res;+}++VkResult VmaAllocator_T::AllocateDedicatedMemory(+    VmaPool pool,+    VkDeviceSize size,+    VmaSuballocationType suballocType,+    VmaDedicatedAllocationList& dedicatedAllocations,+    uint32_t memTypeIndex,+    bool map,+    bool isUserDataString,+    bool isMappingAllowed,+    bool canAliasMemory,+    void* pUserData,+    float priority,+    VkBuffer dedicatedBuffer,+    VkImage dedicatedImage,+    VmaBufferImageUsage dedicatedBufferImageUsage,+    size_t allocationCount,+    VmaAllocation* pAllocations,+    const void* pNextChain)+{+    VMA_ASSERT(allocationCount > 0 && pAllocations);++    VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };+    allocInfo.memoryTypeIndex = memTypeIndex;+    allocInfo.allocationSize = size;+    allocInfo.pNext = pNextChain;++#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR };+    if(!canAliasMemory)+    {+        if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+        {+            if(dedicatedBuffer != VK_NULL_HANDLE)+            {+                VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE);+                dedicatedAllocInfo.buffer = dedicatedBuffer;+                VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);+            }+            else if(dedicatedImage != VK_NULL_HANDLE)+            {+                dedicatedAllocInfo.image = dedicatedImage;+                VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo);+            }+        }+    }+#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000++#if VMA_BUFFER_DEVICE_ADDRESS+    VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR };+    if(m_UseKhrBufferDeviceAddress)+    {+        bool canContainBufferWithDeviceAddress = true;+        if(dedicatedBuffer != VK_NULL_HANDLE)+        {+            canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == VmaBufferImageUsage::UNKNOWN ||+                dedicatedBufferImageUsage.Contains(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT);+        }+        else if(dedicatedImage != VK_NULL_HANDLE)+        {+            canContainBufferWithDeviceAddress = false;+        }+        if(canContainBufferWithDeviceAddress)+        {+            allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR;+            VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo);+        }+    }+#endif // #if VMA_BUFFER_DEVICE_ADDRESS++#if VMA_MEMORY_PRIORITY+    VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT };+    if(m_UseExtMemoryPriority)+    {+        VMA_ASSERT(priority >= 0.F && priority <= 1.F);+        priorityInfo.priority = priority;+        VmaPnextChainPushFront(&allocInfo, &priorityInfo);+    }+#endif // #if VMA_MEMORY_PRIORITY++#if VMA_EXTERNAL_MEMORY+    // Attach VkExportMemoryAllocateInfoKHR if necessary.+    VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR };+    exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex);+    if(exportMemoryAllocInfo.handleTypes != 0)+    {+        VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo);+    }+#endif // #if VMA_EXTERNAL_MEMORY++    size_t allocIndex = 0;+    VkResult res = VK_SUCCESS;+    for(; allocIndex < allocationCount; ++allocIndex)+    {+        res = AllocateDedicatedMemoryPage(+            pool,+            size,+            suballocType,+            memTypeIndex,+            allocInfo,+            map,+            isUserDataString,+            isMappingAllowed,+            pUserData,+            pAllocations + allocIndex);+        if(res != VK_SUCCESS)+        {+            break;+        }+    }++    if(res == VK_SUCCESS)+    {+        for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex)+        {+            dedicatedAllocations.Register(pAllocations[allocIndex]);+        }+        VMA_DEBUG_LOG_FORMAT("    Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%" PRIu32, allocationCount, memTypeIndex);+    }+    else+    {+        // Free all already created allocations.+        while(allocIndex--)+        {+            VmaAllocation currAlloc = pAllocations[allocIndex];+            VkDeviceMemory hMemory = currAlloc->GetMemory();++            /*+            There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory+            before vkFreeMemory.++            if(currAlloc->GetMappedData() != VMA_NULL)+            {+                (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);+            }+            */++            FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory);+            m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize());+            m_AllocationObjectAllocator.Free(currAlloc);+        }++        memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);+    }++    return res;+}++VkResult VmaAllocator_T::AllocateDedicatedMemoryPage(+    VmaPool pool,+    VkDeviceSize size,+    VmaSuballocationType suballocType,+    uint32_t memTypeIndex,+    const VkMemoryAllocateInfo& allocInfo,+    bool map,+    bool isUserDataString,+    bool isMappingAllowed,+    void* pUserData,+    VmaAllocation* pAllocation)+{+    VkDeviceMemory hMemory = VK_NULL_HANDLE;+    VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory);+    if(res < 0)+    {+        VMA_DEBUG_LOG("    vkAllocateMemory FAILED");+        return res;+    }++    void* pMappedData = VMA_NULL;+    if(map)+    {+        res = (*m_VulkanFunctions.vkMapMemory)(+            m_hDevice,+            hMemory,+            0,+            VK_WHOLE_SIZE,+            0,+            &pMappedData);+        if(res < 0)+        {+            VMA_DEBUG_LOG("    vkMapMemory FAILED");+            FreeVulkanMemory(memTypeIndex, size, hMemory);+            return res;+        }+    }++    *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed);+    (*pAllocation)->InitDedicatedAllocation(this, pool, memTypeIndex, hMemory, suballocType, pMappedData, size);+    if (isUserDataString)+        (*pAllocation)->SetName(this, (const char*)pUserData);+    else+        (*pAllocation)->SetUserData(this, pUserData);+    m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size);+    if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)+    {+        FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);+    }++    return VK_SUCCESS;+}++void VmaAllocator_T::GetBufferMemoryRequirements(+    VkBuffer hBuffer,+    VkMemoryRequirements& memReq,+    bool& requiresDedicatedAllocation,+    bool& prefersDedicatedAllocation) const+{+#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR };+        memReqInfo.buffer = hBuffer;++        VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };++        VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };+        VmaPnextChainPushFront(&memReq2, &memDedicatedReq);++        (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);++        memReq = memReq2.memoryRequirements;+        requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);+        prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);+    }+    else+#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    {+        (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq);+        requiresDedicatedAllocation = false;+        prefersDedicatedAllocation  = false;+    }+}++void VmaAllocator_T::GetImageMemoryRequirements(+    VkImage hImage,+    VkMemoryRequirements& memReq,+    bool& requiresDedicatedAllocation,+    bool& prefersDedicatedAllocation) const+{+#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR };+        memReqInfo.image = hImage;++        VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR };++        VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR };+        VmaPnextChainPushFront(&memReq2, &memDedicatedReq);++        (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2);++        memReq = memReq2.memoryRequirements;+        requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE);+        prefersDedicatedAllocation  = (memDedicatedReq.prefersDedicatedAllocation  != VK_FALSE);+    }+    else+#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000+    {+        (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq);+        requiresDedicatedAllocation = false;+        prefersDedicatedAllocation  = false;+    }+}++VkResult VmaAllocator_T::FindMemoryTypeIndex(+    uint32_t memoryTypeBits,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    VmaBufferImageUsage bufImgUsage,+    uint32_t* pMemoryTypeIndex) const+{+    memoryTypeBits &= GetGlobalMemoryTypeBits();++    if(pAllocationCreateInfo->memoryTypeBits != 0)+    {+        memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits;+    }++    VkMemoryPropertyFlags requiredFlags = 0;+    VkMemoryPropertyFlags preferredFlags = 0;+    VkMemoryPropertyFlags notPreferredFlags = 0;+    if(!FindMemoryPreferences(+        IsIntegratedGpu(),+        *pAllocationCreateInfo,+        bufImgUsage,+        requiredFlags, preferredFlags, notPreferredFlags))+    {+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }++    *pMemoryTypeIndex = UINT32_MAX;+    uint32_t minCost = UINT32_MAX;+    for(uint32_t memTypeIndex = 0, memTypeBit = 1;+        memTypeIndex < GetMemoryTypeCount();+        ++memTypeIndex, memTypeBit <<= 1)+    {+        // This memory type is acceptable according to memoryTypeBits bitmask.+        if((memTypeBit & memoryTypeBits) != 0)+        {+            const VkMemoryPropertyFlags currFlags =+                m_MemProps.memoryTypes[memTypeIndex].propertyFlags;+            // This memory type contains requiredFlags.+            if((requiredFlags & ~currFlags) == 0)+            {+                // Calculate cost as number of bits from preferredFlags not present in this memory type.+                uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) ++                    VMA_COUNT_BITS_SET(currFlags & notPreferredFlags);+                // Remember memory type with lowest cost.+                if(currCost < minCost)+                {+                    *pMemoryTypeIndex = memTypeIndex;+                    if(currCost == 0)+                    {+                        return VK_SUCCESS;+                    }+                    minCost = currCost;+                }+            }+        }+    }+    return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;+}++VkResult VmaAllocator_T::CalcMemTypeParams(+    VmaAllocationCreateInfo& inoutCreateInfo,+    uint32_t memTypeIndex,+    VkDeviceSize size,+    size_t allocationCount)+{+    // If memory type is not HOST_VISIBLE, disable MAPPED.+    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 &&+        (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)+    {+        inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT;+    }++    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&+        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0)+    {+        const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);+        VmaBudget heapBudget = {};+        GetHeapBudgets(&heapBudget, heapIndex, 1);+        if(heapBudget.usage + size * allocationCount > heapBudget.budget)+        {+            return VK_ERROR_OUT_OF_DEVICE_MEMORY;+        }+    }+    return VK_SUCCESS;+}++VkResult VmaAllocator_T::CalcAllocationParams(+    VmaAllocationCreateInfo& inoutCreateInfo,+    bool dedicatedRequired)+{+    VMA_ASSERT((inoutCreateInfo.flags &+        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) !=+        (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) &&+        "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect.");+    VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 ||+        (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) &&+        "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");+    if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST)+    {+        if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0)+        {+            VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 &&+                "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.");+        }+    }++    // If memory is lazily allocated, it should be always dedicated.+    if(dedicatedRequired ||+        inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED)+    {+        inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;+    }++    if(inoutCreateInfo.pool != VK_NULL_HANDLE)+    {+        if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&+            (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)+        {+            VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");+            return VK_ERROR_FEATURE_NOT_PRESENT;+        }+        inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();+    }++    if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&+        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)+    {+        VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense.");+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }++    if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY &&+        (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)+    {+        inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;+    }++    // Non-auto USAGE values imply HOST_ACCESS flags.+    // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools.+    // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*.+    // Otherwise they just protect from assert on mapping.+    if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO &&+        inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE &&+        inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST)+    {+        if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0)+        {+            inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;+        }+    }++    return VK_SUCCESS;+}++VkResult VmaAllocator_T::AllocateMemory(+    const VkMemoryRequirements& vkMemReq,+    bool requiresDedicatedAllocation,+    bool prefersDedicatedAllocation,+    VkBuffer dedicatedBuffer,+    VkImage dedicatedImage,+    VmaBufferImageUsage dedicatedBufferImageUsage,+    const VmaAllocationCreateInfo& createInfo,+    VmaSuballocationType suballocType,+    size_t allocationCount,+    VmaAllocation* pAllocations)+{+    memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount);++    VMA_ASSERT(VmaIsPow2(vkMemReq.alignment));++    if(vkMemReq.size == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VmaAllocationCreateInfo createInfoFinal = createInfo;+    VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation);+    if(res != VK_SUCCESS)+        return res;++    if(createInfoFinal.pool != VK_NULL_HANDLE)+    {+        VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;+        return AllocateMemoryOfType(+            createInfoFinal.pool,+            vkMemReq.size,+            vkMemReq.alignment,+            prefersDedicatedAllocation,+            dedicatedBuffer,+            dedicatedImage,+            dedicatedBufferImageUsage,+            createInfoFinal,+            blockVector.GetMemoryTypeIndex(),+            suballocType,+            createInfoFinal.pool->m_DedicatedAllocations,+            blockVector,+            allocationCount,+            pAllocations);+    }++    // Bit mask of memory Vulkan types acceptable for this allocation.+    uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;+    uint32_t memTypeIndex = UINT32_MAX;+    res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);+    // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.+    if(res != VK_SUCCESS)+        return res;+    +    do+    {+        VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];+        VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");+        res = AllocateMemoryOfType(+            VK_NULL_HANDLE,+            vkMemReq.size,+            vkMemReq.alignment,+            requiresDedicatedAllocation || prefersDedicatedAllocation,+            dedicatedBuffer,+            dedicatedImage,+            dedicatedBufferImageUsage,+            createInfoFinal,+            memTypeIndex,+            suballocType,+            m_DedicatedAllocations[memTypeIndex],+            *blockVector,+            allocationCount,+            pAllocations);+        // Allocation succeeded+        if(res == VK_SUCCESS)+            return VK_SUCCESS;++        // Remove old memTypeIndex from list of possibilities.+        memoryTypeBits &= ~(1U << memTypeIndex);+        // Find alternative memTypeIndex.+        res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex);+    } while(res == VK_SUCCESS);++    // No other matching memory type index could be found.+    // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.+    return VK_ERROR_OUT_OF_DEVICE_MEMORY;+}++void VmaAllocator_T::FreeMemory(+    size_t allocationCount,+    const VmaAllocation* pAllocations)+{+    VMA_ASSERT(pAllocations);++    for(size_t allocIndex = allocationCount; allocIndex--; )+    {+        VmaAllocation allocation = pAllocations[allocIndex];++        if(allocation != VK_NULL_HANDLE)+        {+            if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)+            {+                FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED);+            }++            switch(allocation->GetType())+            {+            case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+                {+                    VmaBlockVector* pBlockVector = VMA_NULL;+                    VmaPool hPool = allocation->GetParentPool();+                    if(hPool != VK_NULL_HANDLE)+                    {+                        pBlockVector = &hPool->m_BlockVector;+                    }+                    else+                    {+                        const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();+                        pBlockVector = m_pBlockVectors[memTypeIndex];+                        VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");+                    }+                    pBlockVector->Free(allocation);+                }+                break;+            case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+                FreeDedicatedMemory(allocation);+                break;+            default:+                VMA_ASSERT(0);+            }+        }+    }+}++void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats)+{+    // Initialize.+    VmaClearDetailedStatistics(pStats->total);+    for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)+        VmaClearDetailedStatistics(pStats->memoryType[i]);+    for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)+        VmaClearDetailedStatistics(pStats->memoryHeap[i]);++    // Process default pools.+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+    {+        VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];+        if (pBlockVector != VMA_NULL)+            pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]);+    }++    // Process custom pools.+    {+        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);+        for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))+        {+            VmaBlockVector& blockVector = pool->m_BlockVector;+            const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();+            blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);+            pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]);+        }+    }++    // Process dedicated allocations.+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+    {+        m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]);+    }++    // Sum from memory types to memory heaps.+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+    {+        const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;+        VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]);+    }++    // Sum from memory heaps to total.+    for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex)+        VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]);++    VMA_ASSERT(pStats->total.statistics.allocationCount == 0 ||+        pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin);+    VMA_ASSERT(pStats->total.unusedRangeCount == 0 ||+        pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin);+}++void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount)+{+#if VMA_MEMORY_BUDGET+    if(m_UseExtMemoryBudget)+    {+        if(m_Budget.m_OperationsSinceBudgetFetch < 30)+        {+            VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex);+            for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)+            {+                const uint32_t heapIndex = firstHeap + i;++                outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];+                outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];+                outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];+                outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];++                if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex])+                {+                    outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] ++                        outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];+                }+                else+                {+                    outBudgets->usage = 0;+                }++                // Have to take MIN with heap size because explicit HeapSizeLimit is included in it.+                outBudgets->budget = VMA_MIN(+                    m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size);+            }+        }+        else+        {+            UpdateVulkanBudget(); // Outside of mutex lock+            GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion+        }+    }+    else+#endif+    {+        for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets)+        {+            const uint32_t heapIndex = firstHeap + i;++            outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex];+            outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex];+            outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex];+            outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex];++            outBudgets->usage = outBudgets->statistics.blockBytes;+            outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.+        }+    }+}++void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)+{+    pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();+    pAllocationInfo->deviceMemory = hAllocation->GetMemory();+    pAllocationInfo->offset = hAllocation->GetOffset();+    pAllocationInfo->size = hAllocation->GetSize();+    pAllocationInfo->pMappedData = hAllocation->GetMappedData();+    pAllocationInfo->pUserData = hAllocation->GetUserData();+    pAllocationInfo->pName = hAllocation->GetName();+}++void VmaAllocator_T::GetAllocationInfo2(VmaAllocation hAllocation, VmaAllocationInfo2* pAllocationInfo)+{+    GetAllocationInfo(hAllocation, &pAllocationInfo->allocationInfo);++    switch (hAllocation->GetType())+    {+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+        pAllocationInfo->blockSize = hAllocation->GetBlock()->m_pMetadata->GetSize();+        pAllocationInfo->dedicatedMemory = VK_FALSE;+        break;+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+        pAllocationInfo->blockSize = pAllocationInfo->allocationInfo.size;+        pAllocationInfo->dedicatedMemory = VK_TRUE;+        break;+    default:+        VMA_ASSERT(0);+    }+}++VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool)+{+    VMA_DEBUG_LOG_FORMAT("  CreatePool: MemoryTypeIndex=%" PRIu32 ", flags=%" PRIu32, pCreateInfo->memoryTypeIndex, pCreateInfo->flags);++    VmaPoolCreateInfo newCreateInfo = *pCreateInfo;++    // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash.+    if(pCreateInfo->pMemoryAllocateNext)+    {+        VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0);+    }++    if(newCreateInfo.maxBlockCount == 0)+    {+        newCreateInfo.maxBlockCount = SIZE_MAX;+    }+    if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }+    // Memory type index out of range or forbidden.+    if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||+        ((1U << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)+    {+        return VK_ERROR_FEATURE_NOT_PRESENT;+    }+    if(newCreateInfo.minAllocationAlignment > 0)+    {+        VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));+    }++    const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);++    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);++    VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();+    if(res != VK_SUCCESS)+    {+        vma_delete(this, *pPool);+        *pPool = VMA_NULL;+        return res;+    }++    // Add to m_Pools.+    {+        VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);+        (*pPool)->SetId(m_NextPoolId++);+        m_Pools.PushBack(*pPool);+    }++    return VK_SUCCESS;+}++void VmaAllocator_T::DestroyPool(VmaPool pool)+{+    // Remove from m_Pools.+    {+        VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex);+        m_Pools.Remove(pool);+    }++    vma_delete(this, pool);+}++void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats)+{+    VmaClearStatistics(*pPoolStats);+    pool->m_BlockVector.AddStatistics(*pPoolStats);+    pool->m_DedicatedAllocations.AddStatistics(*pPoolStats);+}++void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats)+{+    VmaClearDetailedStatistics(*pPoolStats);+    pool->m_BlockVector.AddDetailedStatistics(*pPoolStats);+    pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats);+}++void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)+{+    m_CurrentFrameIndex.store(frameIndex);++#if VMA_MEMORY_BUDGET+    if(m_UseExtMemoryBudget)+    {+        UpdateVulkanBudget();+    }+#endif // #if VMA_MEMORY_BUDGET+}++VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)+{+    return hPool->m_BlockVector.CheckCorruption();+}++VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)+{+    VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT;++    // Process default pools.+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+    {+        VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex];+        if(pBlockVector != VMA_NULL)+        {+            VkResult localRes = pBlockVector->CheckCorruption();+            switch(localRes)+            {+            case VK_ERROR_FEATURE_NOT_PRESENT:+                break;+            case VK_SUCCESS:+                finalRes = VK_SUCCESS;+                break;+            default:+                return localRes;+            }+        }+    }++    // Process custom pools.+    {+        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);+        for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))+        {+            if(((1U << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)+            {+                VkResult localRes = pool->m_BlockVector.CheckCorruption();+                switch(localRes)+                {+                case VK_ERROR_FEATURE_NOT_PRESENT:+                    break;+                case VK_SUCCESS:+                    finalRes = VK_SUCCESS;+                    break;+                default:+                    return localRes;+                }+            }+        }+    }++    return finalRes;+}++VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory)+{+    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex);++#if VMA_DEBUG_DONT_EXCEED_HEAP_SIZE_WITH_ALLOCATION_SIZE+    if (pAllocateInfo->allocationSize > m_MemProps.memoryHeaps[heapIndex].size)+    {+        return VK_ERROR_OUT_OF_DEVICE_MEMORY;+    }+#endif++    AtomicTransactionalIncrement<VMA_ATOMIC_UINT32> deviceMemoryCountIncrement;+    const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount);+#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT+    if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount)+    {+        return VK_ERROR_TOO_MANY_OBJECTS;+    }+#endif++    // HeapSizeLimit is in effect for this heap.+    if((m_HeapSizeLimitMask & (1U << heapIndex)) != 0)+    {+        const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size;+        VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex];+        for(;;)+        {+            const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize;+            if(blockBytesAfterAllocation > heapSize)+            {+                return VK_ERROR_OUT_OF_DEVICE_MEMORY;+            }+            if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation))+            {+                break;+            }+        }+    }+    else+    {+        m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize;+    }+    ++m_Budget.m_BlockCount[heapIndex];++    // VULKAN CALL vkAllocateMemory.+    VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory);++    if(res == VK_SUCCESS)+    {+#if VMA_MEMORY_BUDGET+        ++m_Budget.m_OperationsSinceBudgetFetch;+#endif++        // Informative callback.+        if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)+        {+            (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData);+        }++        deviceMemoryCountIncrement.Commit();+    }+    else+    {+        --m_Budget.m_BlockCount[heapIndex];+        m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize;+    }++    return res;+}++void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory)+{+    // Informative callback.+    if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)+    {+        (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData);+    }++    // VULKAN CALL vkFreeMemory.+    (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks());++    const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType);+    --m_Budget.m_BlockCount[heapIndex];+    m_Budget.m_BlockBytes[heapIndex] -= size;++    --m_DeviceMemoryCount;+}++VkResult VmaAllocator_T::BindVulkanBuffer(+    VkDeviceMemory memory,+    VkDeviceSize memoryOffset,+    VkBuffer buffer,+    const void* pNext) const+{+    if(pNext != VMA_NULL)+    {+#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2+        if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&+            m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL)+        {+            VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR };+            bindBufferMemoryInfo.pNext = pNext;+            bindBufferMemoryInfo.buffer = buffer;+            bindBufferMemoryInfo.memory = memory;+            bindBufferMemoryInfo.memoryOffset = memoryOffset;+            return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);+        }+#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2++        return VK_ERROR_EXTENSION_NOT_PRESENT;+    }+    else+    {+        return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset);+    }+}++VkResult VmaAllocator_T::BindVulkanImage(+    VkDeviceMemory memory,+    VkDeviceSize memoryOffset,+    VkImage image,+    const void* pNext) const+{+    if(pNext != VMA_NULL)+    {+#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2+        if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) &&+            m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL)+        {+            VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR };+            bindBufferMemoryInfo.pNext = pNext;+            bindBufferMemoryInfo.image = image;+            bindBufferMemoryInfo.memory = memory;+            bindBufferMemoryInfo.memoryOffset = memoryOffset;+            return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo);+        }+#endif // #if VMA_BIND_MEMORY2++        return VK_ERROR_EXTENSION_NOT_PRESENT;+    }++    return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset);+}++VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData)+{+    switch(hAllocation->GetType())+    {+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+        {+            VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();+            char *pBytes = VMA_NULL;+            VkResult res = pBlock->Map(this, 1, (void**)&pBytes);+            if(res == VK_SUCCESS)+            {+                *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset();+                hAllocation->BlockAllocMap();+            }+            return res;+        }+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+        return hAllocation->DedicatedAllocMap(this, ppData);+    default:+        VMA_ASSERT(0);+        return VK_ERROR_MEMORY_MAP_FAILED;+    }+}++void VmaAllocator_T::Unmap(VmaAllocation hAllocation)+{+    switch(hAllocation->GetType())+    {+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+        {+            VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();+            hAllocation->BlockAllocUnmap();+            pBlock->Unmap(this, 1);+        }+        break;+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+        hAllocation->DedicatedAllocUnmap(this);+        break;+    default:+        VMA_ASSERT(0);+    }+}++VkResult VmaAllocator_T::BindBufferMemory(+    VmaAllocation hAllocation,+    VkDeviceSize allocationLocalOffset,+    VkBuffer hBuffer,+    const void* pNext)+{+    VkResult res = VK_ERROR_UNKNOWN_COPY;+    switch(hAllocation->GetType())+    {+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+        res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext);+        break;+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+    {+        VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock();+        VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block.");+        res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext);+        break;+    }+    default:+        VMA_ASSERT(0);+    }+    return res;+}++VkResult VmaAllocator_T::BindImageMemory(+    VmaAllocation hAllocation,+    VkDeviceSize allocationLocalOffset,+    VkImage hImage,+    const void* pNext)+{+    VkResult res = VK_ERROR_UNKNOWN_COPY;+    switch(hAllocation->GetType())+    {+    case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+        res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext);+        break;+    case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+    {+        VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock();+        VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block.");+        res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext);+        break;+    }+    default:+        VMA_ASSERT(0);+    }+    return res;+}++VkResult VmaAllocator_T::FlushOrInvalidateAllocation(+    VmaAllocation hAllocation,+    VkDeviceSize offset, VkDeviceSize size,+    VMA_CACHE_OPERATION op)+{+    VkResult res = VK_SUCCESS;++    VkMappedMemoryRange memRange = {};+    if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange))+    {+        switch(op)+        {+        case VMA_CACHE_FLUSH:+            res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange);+            break;+        case VMA_CACHE_INVALIDATE:+            res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange);+            break;+        default:+            VMA_ASSERT(0);+        }+    }+    // else: Just ignore this call.+    return res;+}++VkResult VmaAllocator_T::FlushOrInvalidateAllocations(+    uint32_t allocationCount,+    const VmaAllocation* allocations,+    const VkDeviceSize* offsets, const VkDeviceSize* sizes,+    VMA_CACHE_OPERATION op)+{+    typedef VmaStlAllocator<VkMappedMemoryRange> RangeAllocator;+    typedef VmaSmallVector<VkMappedMemoryRange, RangeAllocator, 16> RangeVector;+    RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks()));++    for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)+    {+        const VmaAllocation alloc = allocations[allocIndex];+        const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0;+        const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE;+        VkMappedMemoryRange newRange;+        if(GetFlushOrInvalidateRange(alloc, offset, size, newRange))+        {+            ranges.push_back(newRange);+        }+    }++    VkResult res = VK_SUCCESS;+    if(!ranges.empty())+    {+        switch(op)+        {+        case VMA_CACHE_FLUSH:+            res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());+            break;+        case VMA_CACHE_INVALIDATE:+            res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data());+            break;+        default:+            VMA_ASSERT(0);+        }+    }+    // else: Just ignore this call.+    return res;+}++VkResult VmaAllocator_T::CopyMemoryToAllocation(+    const void* pSrcHostPointer,+    VmaAllocation dstAllocation,+    VkDeviceSize dstAllocationLocalOffset,+    VkDeviceSize size)+{+    void* dstMappedData = VMA_NULL;+    VkResult res = Map(dstAllocation, &dstMappedData);+    if(res == VK_SUCCESS)+    {+        memcpy((char*)dstMappedData + dstAllocationLocalOffset, pSrcHostPointer, (size_t)size);+        Unmap(dstAllocation);+        res = FlushOrInvalidateAllocation(dstAllocation, dstAllocationLocalOffset, size, VMA_CACHE_FLUSH);+    }+    return res;+}++VkResult VmaAllocator_T::CopyAllocationToMemory(+    VmaAllocation srcAllocation,+    VkDeviceSize srcAllocationLocalOffset,+    void* pDstHostPointer,+    VkDeviceSize size)+{+    void* srcMappedData = VMA_NULL;+    VkResult res = Map(srcAllocation, &srcMappedData);+    if(res == VK_SUCCESS)+    {+        res = FlushOrInvalidateAllocation(srcAllocation, srcAllocationLocalOffset, size, VMA_CACHE_INVALIDATE);+        if(res == VK_SUCCESS)+        {+            memcpy(pDstHostPointer, (const char*)srcMappedData + srcAllocationLocalOffset, (size_t)size);+            Unmap(srcAllocation);+        }+    }+    return res;+}++void VmaAllocator_T::FreeDedicatedMemory(VmaAllocation allocation)+{+    VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);++    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();+    VmaPool parentPool = allocation->GetParentPool();+    if(parentPool == VK_NULL_HANDLE)+    {+        // Default pool+        m_DedicatedAllocations[memTypeIndex].Unregister(allocation);+    }+    else+    {+        // Custom pool+        parentPool->m_DedicatedAllocations.Unregister(allocation);+    }++    VkDeviceMemory hMemory = allocation->GetMemory();++    /*+    There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory+    before vkFreeMemory.++    if(allocation->GetMappedData() != VMA_NULL)+    {+        (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory);+    }+    */++    FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory);++    m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize());+    allocation->Destroy(this);+    m_AllocationObjectAllocator.Free(allocation);++    VMA_DEBUG_LOG_FORMAT("    Freed DedicatedMemory MemoryTypeIndex=%" PRIu32, memTypeIndex);+}++uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const+{+    VkBufferCreateInfo dummyBufCreateInfo;+    VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo);++    uint32_t memoryTypeBits = 0;++    // Create buffer.+    VkBuffer buf = VK_NULL_HANDLE;+    VkResult res = (*GetVulkanFunctions().vkCreateBuffer)(+        m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf);+    if(res == VK_SUCCESS)+    {+        // Query for supported memory types.+        VkMemoryRequirements memReq;+        (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq);+        memoryTypeBits = memReq.memoryTypeBits;++        // Destroy buffer.+        (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks());+    }++    return memoryTypeBits;+}++uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const+{+    // Make sure memory information is already fetched.+    VMA_ASSERT(GetMemoryTypeCount() > 0);++    uint32_t memoryTypeBits = UINT32_MAX;++    if(!m_UseAmdDeviceCoherentMemory)+    {+        // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD.+        for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+        {+            if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0)+            {+                memoryTypeBits &= ~(1U << memTypeIndex);+            }+        }+    }++    return memoryTypeBits;+}++bool VmaAllocator_T::GetFlushOrInvalidateRange(+    VmaAllocation allocation,+    VkDeviceSize offset, VkDeviceSize size,+    VkMappedMemoryRange& outRange) const+{+    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();+    if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex))+    {+        const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;+        const VkDeviceSize allocationSize = allocation->GetSize();+        VMA_ASSERT(offset <= allocationSize);++        outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;+        outRange.pNext = VMA_NULL;+        outRange.memory = allocation->GetMemory();++        switch(allocation->GetType())+        {+        case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED:+            outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);+            if(size == VK_WHOLE_SIZE)+            {+                outRange.size = allocationSize - outRange.offset;+            }+            else+            {+                VMA_ASSERT(offset + size <= allocationSize);+                outRange.size = VMA_MIN(+                    VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize),+                    allocationSize - outRange.offset);+            }+            break;+        case VmaAllocation_T::ALLOCATION_TYPE_BLOCK:+        {+            // 1. Still within this allocation.+            outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize);+            if(size == VK_WHOLE_SIZE)+            {+                size = allocationSize - offset;+            }+            else+            {+                VMA_ASSERT(offset + size <= allocationSize);+            }+            outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize);++            // 2. Adjust to whole block.+            const VkDeviceSize allocationOffset = allocation->GetOffset();+            VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0);+            const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize();+            outRange.offset += allocationOffset;+            outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset);++            break;+        }+        default:+            VMA_ASSERT(0);+        }+        return true;+    }+    return false;+}++#if VMA_MEMORY_BUDGET+void VmaAllocator_T::UpdateVulkanBudget()+{+    VMA_ASSERT(m_UseExtMemoryBudget);++    VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR };++    VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT };+    VmaPnextChainPushFront(&memProps, &budgetProps);++    GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps);++    {+        VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex);++        for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex)+        {+            m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex];+            m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex];+            m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load();++            // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size.+            if(m_Budget.m_VulkanBudget[heapIndex] == 0)+            {+                m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics.+            }+            else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size)+            {+                m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size;+            }+            if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0)+            {+                m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex];+            }+        }+        m_Budget.m_OperationsSinceBudgetFetch = 0;+    }+}+#endif // VMA_MEMORY_BUDGET++void VmaAllocator_T::FillAllocation(VmaAllocation hAllocation, uint8_t pattern)+{+    if(VMA_DEBUG_INITIALIZE_ALLOCATIONS &&+        hAllocation->IsMappingAllowed() &&+        (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)+    {+        void* pData = VMA_NULL;+        VkResult res = Map(hAllocation, &pData);+        if(res == VK_SUCCESS)+        {+            memset(pData, (int)pattern, (size_t)hAllocation->GetSize());+            FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH);+            Unmap(hAllocation);+        }+        else+        {+            VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation.");+        }+    }+}++uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits()+{+    uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load();+    if(memoryTypeBits == UINT32_MAX)+    {+        memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits();+        m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits);+    }+    return memoryTypeBits;+}++#if VMA_STATS_STRING_ENABLED+void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)+{+    json.WriteString("DefaultPools");+    json.BeginObject();+    {+        for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+        {+            VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex];+            VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex];+            if (pBlockVector != VMA_NULL)+            {+                json.BeginString("Type ");+                json.ContinueString(memTypeIndex);+                json.EndString();+                json.BeginObject();+                {+                    json.WriteString("PreferredBlockSize");+                    json.WriteNumber(pBlockVector->GetPreferredBlockSize());++                    json.WriteString("Blocks");+                    pBlockVector->PrintDetailedMap(json);++                    json.WriteString("DedicatedAllocations");+                    dedicatedAllocList.BuildStatsString(json);+                }+                json.EndObject();+            }+        }+    }+    json.EndObject();++    json.WriteString("CustomPools");+    json.BeginObject();+    {+        VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);+        if (!m_Pools.IsEmpty())+        {+            for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)+            {+                bool displayType = true;+                size_t index = 0;+                for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))+                {+                    VmaBlockVector& blockVector = pool->m_BlockVector;+                    if (blockVector.GetMemoryTypeIndex() == memTypeIndex)+                    {+                        if (displayType)+                        {+                            json.BeginString("Type ");+                            json.ContinueString(memTypeIndex);+                            json.EndString();+                            json.BeginArray();+                            displayType = false;+                        }++                        json.BeginObject();+                        {+                            json.WriteString("Name");+                            json.BeginString();+                            json.ContinueString((uint64_t)index++);+                            if (pool->GetName())+                            {+                                json.ContinueString(" - ");+                                json.ContinueString(pool->GetName());+                            }+                            json.EndString();++                            json.WriteString("PreferredBlockSize");+                            json.WriteNumber(blockVector.GetPreferredBlockSize());++                            json.WriteString("Blocks");+                            blockVector.PrintDetailedMap(json);++                            json.WriteString("DedicatedAllocations");+                            pool->m_DedicatedAllocations.BuildStatsString(json);+                        }+                        json.EndObject();+                    }+                }++                if (!displayType)+                    json.EndArray();+            }+        }+    }+    json.EndObject();+}+#endif // VMA_STATS_STRING_ENABLED+#endif // _VMA_ALLOCATOR_T_FUNCTIONS+++#ifndef _VMA_PUBLIC_INTERFACE++#ifdef VOLK_HEADER_VERSION++VMA_CALL_PRE VkResult VMA_CALL_POST vmaImportVulkanFunctionsFromVolk(+    const VmaAllocatorCreateInfo* VMA_NOT_NULL pAllocatorCreateInfo,+    VmaVulkanFunctions* VMA_NOT_NULL pDstVulkanFunctions)+{+    VMA_ASSERT(pAllocatorCreateInfo != VMA_NULL);+    VMA_ASSERT(pAllocatorCreateInfo->instance != VK_NULL_HANDLE);+    VMA_ASSERT(pAllocatorCreateInfo->device != VK_NULL_HANDLE);++    memset(pDstVulkanFunctions, 0, sizeof(*pDstVulkanFunctions));+    +    VolkDeviceTable src = {};+    volkLoadDeviceTable(&src, pAllocatorCreateInfo->device);++#define COPY_GLOBAL_TO_VMA_FUNC(volkName, vmaName) if(!pDstVulkanFunctions->vmaName) pDstVulkanFunctions->vmaName = volkName;+#define COPY_DEVICE_TO_VMA_FUNC(volkName, vmaName) if(!pDstVulkanFunctions->vmaName) pDstVulkanFunctions->vmaName = src.volkName;++    COPY_GLOBAL_TO_VMA_FUNC(vkGetInstanceProcAddr, vkGetInstanceProcAddr)+    COPY_GLOBAL_TO_VMA_FUNC(vkGetDeviceProcAddr, vkGetDeviceProcAddr)+    COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceProperties, vkGetPhysicalDeviceProperties)+    COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties, vkGetPhysicalDeviceMemoryProperties)+    COPY_DEVICE_TO_VMA_FUNC(vkAllocateMemory, vkAllocateMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkFreeMemory, vkFreeMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkMapMemory, vkMapMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkUnmapMemory, vkUnmapMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkFlushMappedMemoryRanges, vkFlushMappedMemoryRanges)+    COPY_DEVICE_TO_VMA_FUNC(vkInvalidateMappedMemoryRanges, vkInvalidateMappedMemoryRanges)+    COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory, vkBindBufferMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory, vkBindImageMemory)+    COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements, vkGetBufferMemoryRequirements)+    COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements, vkGetImageMemoryRequirements)+    COPY_DEVICE_TO_VMA_FUNC(vkCreateBuffer, vkCreateBuffer)+    COPY_DEVICE_TO_VMA_FUNC(vkDestroyBuffer, vkDestroyBuffer)+    COPY_DEVICE_TO_VMA_FUNC(vkCreateImage, vkCreateImage)+    COPY_DEVICE_TO_VMA_FUNC(vkDestroyImage, vkDestroyImage)+    COPY_DEVICE_TO_VMA_FUNC(vkCmdCopyBuffer, vkCmdCopyBuffer)+#if VMA_VULKAN_VERSION >= 1001000+    if (pAllocatorCreateInfo->vulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0))+    {+        COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties2, vkGetPhysicalDeviceMemoryProperties2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements2, vkGetBufferMemoryRequirements2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements2, vkGetImageMemoryRequirements2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory2, vkBindBufferMemory2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory2, vkBindImageMemory2KHR)+    }+#endif+#if VMA_VULKAN_VERSION >= 1003000+    if (pAllocatorCreateInfo->vulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0))+    {+        COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceBufferMemoryRequirements, vkGetDeviceBufferMemoryRequirements)+        COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceImageMemoryRequirements, vkGetDeviceImageMemoryRequirements)+    }+#endif+#if VMA_KHR_MAINTENANCE4+    if((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT) != 0)+    {+        COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceBufferMemoryRequirementsKHR, vkGetDeviceBufferMemoryRequirements)+        COPY_DEVICE_TO_VMA_FUNC(vkGetDeviceImageMemoryRequirementsKHR, vkGetDeviceImageMemoryRequirements)+    }+#endif+#if VMA_DEDICATED_ALLOCATION+    if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0)+    {+        COPY_DEVICE_TO_VMA_FUNC(vkGetBufferMemoryRequirements2KHR, vkGetBufferMemoryRequirements2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkGetImageMemoryRequirements2KHR, vkGetImageMemoryRequirements2KHR)+    }+#endif+#if VMA_BIND_MEMORY2+    if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0)+    {+        COPY_DEVICE_TO_VMA_FUNC(vkBindBufferMemory2KHR, vkBindBufferMemory2KHR)+        COPY_DEVICE_TO_VMA_FUNC(vkBindImageMemory2KHR, vkBindImageMemory2KHR)+    }+#endif+#if VMA_MEMORY_BUDGET+    if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0)+    {+        COPY_GLOBAL_TO_VMA_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, vkGetPhysicalDeviceMemoryProperties2KHR)+    }+#endif+#if VMA_EXTERNAL_MEMORY_WIN32+    if ((pAllocatorCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT) != 0)+    {+        COPY_DEVICE_TO_VMA_FUNC(vkGetMemoryWin32HandleKHR, vkGetMemoryWin32HandleKHR)+    }+#endif++#undef COPY_DEVICE_TO_VMA_FUNC+#undef COPY_GLOBAL_TO_VMA_FUNC++    return VK_SUCCESS;+}++#endif // #ifdef VOLK_HEADER_VERSION++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator(+    const VmaAllocatorCreateInfo* pCreateInfo,+    VmaAllocator* pAllocator)+{+    VMA_ASSERT(pCreateInfo && pAllocator);+    VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 ||+        (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 4));+    VMA_DEBUG_LOG("vmaCreateAllocator");+    *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);+    VkResult result = (*pAllocator)->Init(pCreateInfo);+    if(result < 0)+    {+        vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator);+        *pAllocator = VK_NULL_HANDLE;+    }+    return result;+}++VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator(+    VmaAllocator allocator)+{+    if(allocator != VK_NULL_HANDLE)+    {+        VMA_DEBUG_LOG("vmaDestroyAllocator");+        VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying.+        vma_delete(&allocationCallbacks, allocator);+    }+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo)+{+    VMA_ASSERT(allocator && pAllocatorInfo);+    pAllocatorInfo->instance = allocator->m_hInstance;+    pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice();+    pAllocatorInfo->device = allocator->m_hDevice;+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties(+    VmaAllocator allocator,+    const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)+{+    VMA_ASSERT(allocator && ppPhysicalDeviceProperties);+    *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties(+    VmaAllocator allocator,+    const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)+{+    VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);+    *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties(+    VmaAllocator allocator,+    uint32_t memoryTypeIndex,+    VkMemoryPropertyFlags* pFlags)+{+    VMA_ASSERT(allocator && pFlags);+    VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());+    *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;+}++VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex(+    VmaAllocator allocator,+    uint32_t frameIndex)+{+    VMA_ASSERT(allocator);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->SetCurrentFrameIndex(frameIndex);+}++VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics(+    VmaAllocator allocator,+    VmaTotalStatistics* pStats)+{+    VMA_ASSERT(allocator && pStats);+    VMA_DEBUG_GLOBAL_MUTEX_LOCK+    allocator->CalculateStatistics(pStats);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets(+    VmaAllocator allocator,+    VmaBudget* pBudgets)+{+    VMA_ASSERT(allocator && pBudgets);+    VMA_DEBUG_GLOBAL_MUTEX_LOCK+    allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount());+}++#if VMA_STATS_STRING_ENABLED++VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString(+    VmaAllocator allocator,+    char** ppStatsString,+    VkBool32 detailedMap)+{+    VMA_ASSERT(allocator && ppStatsString);+    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VmaStringBuilder sb(allocator->GetAllocationCallbacks());+    {+        VmaBudget budgets[VK_MAX_MEMORY_HEAPS];+        allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount());++        VmaTotalStatistics stats;+        allocator->CalculateStatistics(&stats);++        VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb);+        json.BeginObject();+        {+            json.WriteString("General");+            json.BeginObject();+            {+                const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties;+                const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps;++                json.WriteString("API");+                json.WriteString("Vulkan");++                json.WriteString("apiVersion");+                json.BeginString();+                json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion));+                json.ContinueString(".");+                json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion));+                json.ContinueString(".");+                json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion));+                json.EndString();++                json.WriteString("GPU");+                json.WriteString(deviceProperties.deviceName);+                json.WriteString("deviceType");+                json.WriteNumber(static_cast<uint32_t>(deviceProperties.deviceType));++                json.WriteString("maxMemoryAllocationCount");+                json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount);+                json.WriteString("bufferImageGranularity");+                json.WriteNumber(deviceProperties.limits.bufferImageGranularity);+                json.WriteString("nonCoherentAtomSize");+                json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize);++                json.WriteString("memoryHeapCount");+                json.WriteNumber(memoryProperties.memoryHeapCount);+                json.WriteString("memoryTypeCount");+                json.WriteNumber(memoryProperties.memoryTypeCount);+            }+            json.EndObject();+        }+        {+            json.WriteString("Total");+            VmaPrintDetailedStatistics(json, stats.total);+        }+        {+            json.WriteString("MemoryInfo");+            json.BeginObject();+            {+                for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)+                {+                    json.BeginString("Heap ");+                    json.ContinueString(heapIndex);+                    json.EndString();+                    json.BeginObject();+                    {+                        const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex];+                        json.WriteString("Flags");+                        json.BeginArray(true);+                        {+                            if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)+                                json.WriteString("DEVICE_LOCAL");+                        #if VMA_VULKAN_VERSION >= 1001000+                            if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT)+                                json.WriteString("MULTI_INSTANCE");+                        #endif++                            VkMemoryHeapFlags flags = heapInfo.flags &+                                ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT+                        #if VMA_VULKAN_VERSION >= 1001000+                                    | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT+                        #endif+                                    );+                            if (flags != 0)+                                json.WriteNumber(flags);+                        }+                        json.EndArray();++                        json.WriteString("Size");+                        json.WriteNumber(heapInfo.size);++                        json.WriteString("Budget");+                        json.BeginObject();+                        {+                            json.WriteString("BudgetBytes");+                            json.WriteNumber(budgets[heapIndex].budget);+                            json.WriteString("UsageBytes");+                            json.WriteNumber(budgets[heapIndex].usage);+                        }+                        json.EndObject();++                        json.WriteString("Stats");+                        VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]);++                        json.WriteString("MemoryPools");+                        json.BeginObject();+                        {+                            for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)+                            {+                                if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex)+                                {+                                    json.BeginString("Type ");+                                    json.ContinueString(typeIndex);+                                    json.EndString();+                                    json.BeginObject();+                                    {+                                        json.WriteString("Flags");+                                        json.BeginArray(true);+                                        {+                                            VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;+                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)+                                                json.WriteString("DEVICE_LOCAL");+                                            if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)+                                                json.WriteString("HOST_VISIBLE");+                                            if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)+                                                json.WriteString("HOST_COHERENT");+                                            if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)+                                                json.WriteString("HOST_CACHED");+                                            if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT)+                                                json.WriteString("LAZILY_ALLOCATED");+                                        #if VMA_VULKAN_VERSION >= 1001000+                                            if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT)+                                                json.WriteString("PROTECTED");+                                        #endif+                                        #if VK_AMD_device_coherent_memory+                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY)+                                                json.WriteString("DEVICE_COHERENT_AMD");+                                            if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)+                                                json.WriteString("DEVICE_UNCACHED_AMD");+                                        #endif++                                            flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT+                                        #if VMA_VULKAN_VERSION >= 1001000+                                                | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT+                                        #endif+                                        #if VK_AMD_device_coherent_memory+                                                | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY+                                                | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY+                                        #endif+                                                | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT+                                                | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT+                                                | VK_MEMORY_PROPERTY_HOST_CACHED_BIT);+                                            if (flags != 0)+                                                json.WriteNumber(flags);+                                        }+                                        json.EndArray();++                                        json.WriteString("Stats");+                                        VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]);+                                    }+                                    json.EndObject();+                                }+                            }++                        }+                        json.EndObject();+                    }+                    json.EndObject();+                }+            }+            json.EndObject();+        }++        if (detailedMap == VK_TRUE)+            allocator->PrintDetailedMap(json);++        json.EndObject();+    }++    *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength());+}++VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString(+    VmaAllocator allocator,+    char* pStatsString)+{+    if(pStatsString != VMA_NULL)+    {+        VMA_ASSERT(allocator);+        VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString);+    }+}++#endif // VMA_STATS_STRING_ENABLED++/*+This function is not protected by any mutex because it just reads immutable data.+*/+VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex(+    VmaAllocator allocator,+    uint32_t memoryTypeBits,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    uint32_t* pMemoryTypeIndex)+{+    VMA_ASSERT(allocator != VK_NULL_HANDLE);+    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);+    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);++    return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, VmaBufferImageUsage::UNKNOWN, pMemoryTypeIndex);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo(+    VmaAllocator allocator,+    const VkBufferCreateInfo* pBufferCreateInfo,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    uint32_t* pMemoryTypeIndex)+{+    VMA_ASSERT(allocator != VK_NULL_HANDLE);+    VMA_ASSERT(pBufferCreateInfo != VMA_NULL);+    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);+    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);++    const VkDevice hDev = allocator->m_hDevice;+    const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();+    VkResult res = VK_SUCCESS;++#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    if(funcs->vkGetDeviceBufferMemoryRequirements)+    {+        // Can query straight from VkBufferCreateInfo :)+        VkDeviceBufferMemoryRequirementsKHR devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS_KHR};+        devBufMemReq.pCreateInfo = pBufferCreateInfo;++        VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};+        (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq);++        res = allocator->FindMemoryTypeIndex(+            memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo,+            VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex);+    }+    else+#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    {+        // Must create a dummy buffer to query :(+        VkBuffer hBuffer = VK_NULL_HANDLE;+        res = funcs->vkCreateBuffer(+            hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer);+        if(res == VK_SUCCESS)+        {+            VkMemoryRequirements memReq = {};+            funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq);++            res = allocator->FindMemoryTypeIndex(+                memReq.memoryTypeBits, pAllocationCreateInfo,+                VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), pMemoryTypeIndex);++            funcs->vkDestroyBuffer(+                hDev, hBuffer, allocator->GetAllocationCallbacks());+        }+    }+    return res;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo(+    VmaAllocator allocator,+    const VkImageCreateInfo* pImageCreateInfo,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    uint32_t* pMemoryTypeIndex)+{+    VMA_ASSERT(allocator != VK_NULL_HANDLE);+    VMA_ASSERT(pImageCreateInfo != VMA_NULL);+    VMA_ASSERT(pAllocationCreateInfo != VMA_NULL);+    VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);++    const VkDevice hDev = allocator->m_hDevice;+    const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions();+    VkResult res = VK_SUCCESS;++#if VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    if(funcs->vkGetDeviceImageMemoryRequirements)+    {+        // Can query straight from VkImageCreateInfo :)+        VkDeviceImageMemoryRequirementsKHR devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS_KHR};+        devImgMemReq.pCreateInfo = pImageCreateInfo;+        VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&+            "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect.");++        VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2};+        (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq);++        res = allocator->FindMemoryTypeIndex(+            memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo,+            VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex);+    }+    else+#endif // VMA_KHR_MAINTENANCE4 || VMA_VULKAN_VERSION >= 1003000+    {+        // Must create a dummy image to query :(+        VkImage hImage = VK_NULL_HANDLE;+        res = funcs->vkCreateImage(+            hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage);+        if(res == VK_SUCCESS)+        {+            VkMemoryRequirements memReq = {};+            funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq);++            res = allocator->FindMemoryTypeIndex(+                memReq.memoryTypeBits, pAllocationCreateInfo,+                VmaBufferImageUsage(*pImageCreateInfo), pMemoryTypeIndex);++            funcs->vkDestroyImage(+                hDev, hImage, allocator->GetAllocationCallbacks());+        }+    }+    return res;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool(+    VmaAllocator allocator,+    const VmaPoolCreateInfo* pCreateInfo,+    VmaPool* pPool)+{+    VMA_ASSERT(allocator && pCreateInfo && pPool);++    VMA_DEBUG_LOG("vmaCreatePool");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->CreatePool(pCreateInfo, pPool);+}++VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool(+    VmaAllocator allocator,+    VmaPool pool)+{+    VMA_ASSERT(allocator);++    if(pool == VK_NULL_HANDLE)+    {+        return;+    }++    VMA_DEBUG_LOG("vmaDestroyPool");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->DestroyPool(pool);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics(+    VmaAllocator allocator,+    VmaPool pool,+    VmaStatistics* pPoolStats)+{+    VMA_ASSERT(allocator && pool && pPoolStats);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->GetPoolStatistics(pool, pPoolStats);+}++VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics(+    VmaAllocator allocator,+    VmaPool pool,+    VmaDetailedStatistics* pPoolStats)+{+    VMA_ASSERT(allocator && pool && pPoolStats);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->CalculatePoolStatistics(pool, pPoolStats);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool)+{+    VMA_ASSERT(allocator && pool);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VMA_DEBUG_LOG("vmaCheckPoolCorruption");++    return allocator->CheckPoolCorruption(pool);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName(+    VmaAllocator allocator,+    VmaPool pool,+    const char** ppName)+{+    VMA_ASSERT(allocator && pool && ppName);++    VMA_DEBUG_LOG("vmaGetPoolName");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    *ppName = pool->GetName();+}++VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName(+    VmaAllocator allocator,+    VmaPool pool,+    const char* pName)+{+    VMA_ASSERT(allocator && pool);++    VMA_DEBUG_LOG("vmaSetPoolName");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    pool->SetName(pName);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory(+    VmaAllocator allocator,+    const VkMemoryRequirements* pVkMemoryRequirements,+    const VmaAllocationCreateInfo* pCreateInfo,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation);++    VMA_DEBUG_LOG("vmaAllocateMemory");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VkResult result = allocator->AllocateMemory(+        *pVkMemoryRequirements,+        false, // requiresDedicatedAllocation+        false, // prefersDedicatedAllocation+        VK_NULL_HANDLE, // dedicatedBuffer+        VK_NULL_HANDLE, // dedicatedImage+        VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage+        *pCreateInfo,+        VMA_SUBALLOCATION_TYPE_UNKNOWN,+        1, // allocationCount+        pAllocation);++    if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)+    {+        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+    }++    return result;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages(+    VmaAllocator allocator,+    const VkMemoryRequirements* pVkMemoryRequirements,+    const VmaAllocationCreateInfo* pCreateInfo,+    size_t allocationCount,+    VmaAllocation* pAllocations,+    VmaAllocationInfo* pAllocationInfo)+{+    if(allocationCount == 0)+    {+        return VK_SUCCESS;+    }++    VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations);++    VMA_DEBUG_LOG("vmaAllocateMemoryPages");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VkResult result = allocator->AllocateMemory(+        *pVkMemoryRequirements,+        false, // requiresDedicatedAllocation+        false, // prefersDedicatedAllocation+        VK_NULL_HANDLE, // dedicatedBuffer+        VK_NULL_HANDLE, // dedicatedImage+        VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage+        *pCreateInfo,+        VMA_SUBALLOCATION_TYPE_UNKNOWN,+        allocationCount,+        pAllocations);++    if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS)+    {+        for(size_t i = 0; i < allocationCount; ++i)+        {+            allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i);+        }+    }++    return result;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer(+    VmaAllocator allocator,+    VkBuffer buffer,+    const VmaAllocationCreateInfo* pCreateInfo,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation);++    VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VkMemoryRequirements vkMemReq = {};+    bool requiresDedicatedAllocation = false;+    bool prefersDedicatedAllocation = false;+    allocator->GetBufferMemoryRequirements(buffer, vkMemReq,+        requiresDedicatedAllocation,+        prefersDedicatedAllocation);++    VkResult result = allocator->AllocateMemory(+        vkMemReq,+        requiresDedicatedAllocation,+        prefersDedicatedAllocation,+        buffer, // dedicatedBuffer+        VK_NULL_HANDLE, // dedicatedImage+        VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage+        *pCreateInfo,+        VMA_SUBALLOCATION_TYPE_BUFFER,+        1, // allocationCount+        pAllocation);++    if(pAllocationInfo && result == VK_SUCCESS)+    {+        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+    }++    return result;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage(+    VmaAllocator allocator,+    VkImage image,+    const VmaAllocationCreateInfo* pCreateInfo,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation);++    VMA_DEBUG_LOG("vmaAllocateMemoryForImage");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    VkMemoryRequirements vkMemReq = {};+    bool requiresDedicatedAllocation = false;+    bool prefersDedicatedAllocation  = false;+    allocator->GetImageMemoryRequirements(image, vkMemReq,+        requiresDedicatedAllocation, prefersDedicatedAllocation);++    VkResult result = allocator->AllocateMemory(+        vkMemReq,+        requiresDedicatedAllocation,+        prefersDedicatedAllocation,+        VK_NULL_HANDLE, // dedicatedBuffer+        image, // dedicatedImage+        VmaBufferImageUsage::UNKNOWN, // dedicatedBufferImageUsage+        *pCreateInfo,+        VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,+        1, // allocationCount+        pAllocation);++    if(pAllocationInfo && result == VK_SUCCESS)+    {+        allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+    }++    return result;+}++VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory(+    VmaAllocator allocator,+    VmaAllocation allocation)+{+    VMA_ASSERT(allocator);++    if(allocation == VK_NULL_HANDLE)+    {+        return;+    }++    VMA_DEBUG_LOG("vmaFreeMemory");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->FreeMemory(+        1, // allocationCount+        &allocation);+}++VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages(+    VmaAllocator allocator,+    size_t allocationCount,+    const VmaAllocation* pAllocations)+{+    if(allocationCount == 0)+    {+        return;+    }++    VMA_ASSERT(allocator);++    VMA_DEBUG_LOG("vmaFreeMemoryPages");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->FreeMemory(allocationCount, pAllocations);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && allocation && pAllocationInfo);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->GetAllocationInfo(allocation, pAllocationInfo);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo2(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VmaAllocationInfo2* pAllocationInfo)+{+    VMA_ASSERT(allocator && allocation && pAllocationInfo);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->GetAllocationInfo2(allocation, pAllocationInfo);+}++VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData(+    VmaAllocator allocator,+    VmaAllocation allocation,+    void* pUserData)+{+    VMA_ASSERT(allocator && allocation);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocation->SetUserData(allocator, pUserData);+}++VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const char* VMA_NULLABLE pName)+{+    allocation->SetName(allocator, pName);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkMemoryPropertyFlags* VMA_NOT_NULL pFlags)+{+    VMA_ASSERT(allocator && allocation && pFlags);+    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();+    *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory(+    VmaAllocator allocator,+    VmaAllocation allocation,+    void** ppData)+{+    VMA_ASSERT(allocator && allocation && ppData);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->Map(allocation, ppData);+}++VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory(+    VmaAllocator allocator,+    VmaAllocation allocation)+{+    VMA_ASSERT(allocator && allocation);++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    allocator->Unmap(allocation);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkDeviceSize offset,+    VkDeviceSize size)+{+    VMA_ASSERT(allocator && allocation);++    VMA_DEBUG_LOG("vmaFlushAllocation");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkDeviceSize offset,+    VkDeviceSize size)+{+    VMA_ASSERT(allocator && allocation);++    VMA_DEBUG_LOG("vmaInvalidateAllocation");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(+    VmaAllocator allocator,+    uint32_t allocationCount,+    const VmaAllocation* allocations,+    const VkDeviceSize* offsets,+    const VkDeviceSize* sizes)+{+    VMA_ASSERT(allocator);++    if(allocationCount == 0)+    {+        return VK_SUCCESS;+    }++    VMA_ASSERT(allocations);++    VMA_DEBUG_LOG("vmaFlushAllocations");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(+    VmaAllocator allocator,+    uint32_t allocationCount,+    const VmaAllocation* allocations,+    const VkDeviceSize* offsets,+    const VkDeviceSize* sizes)+{+    VMA_ASSERT(allocator);++    if(allocationCount == 0)+    {+        return VK_SUCCESS;+    }++    VMA_ASSERT(allocations);++    VMA_DEBUG_LOG("vmaInvalidateAllocations");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(+    VmaAllocator allocator,+    const void* pSrcHostPointer,+    VmaAllocation dstAllocation,+    VkDeviceSize dstAllocationLocalOffset,+    VkDeviceSize size)+{+    VMA_ASSERT(allocator && pSrcHostPointer && dstAllocation);++    if(size == 0)+    {+        return VK_SUCCESS;+    }++    VMA_DEBUG_LOG("vmaCopyMemoryToAllocation");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->CopyMemoryToAllocation(pSrcHostPointer, dstAllocation, dstAllocationLocalOffset, size);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(+    VmaAllocator allocator,+    VmaAllocation srcAllocation,+    VkDeviceSize srcAllocationLocalOffset,+    void* pDstHostPointer,+    VkDeviceSize size)+{+    VMA_ASSERT(allocator && srcAllocation && pDstHostPointer);++    if(size == 0)+    {+        return VK_SUCCESS;+    }++    VMA_DEBUG_LOG("vmaCopyAllocationToMemory");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->CopyAllocationToMemory(srcAllocation, srcAllocationLocalOffset, pDstHostPointer, size);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(+    VmaAllocator allocator,+    uint32_t memoryTypeBits)+{+    VMA_ASSERT(allocator);++    VMA_DEBUG_LOG("vmaCheckCorruption");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->CheckCorruption(memoryTypeBits);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation(+    VmaAllocator allocator,+    const VmaDefragmentationInfo* pInfo,+    VmaDefragmentationContext* pContext)+{+    VMA_ASSERT(allocator && pInfo && pContext);++    VMA_DEBUG_LOG("vmaBeginDefragmentation");++    if (pInfo->pool != VMA_NULL)+    {+        // Check if run on supported algorithms+        if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT)+            return VK_ERROR_FEATURE_NOT_PRESENT;+    }++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo);+    return VK_SUCCESS;+}++VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation(+    VmaAllocator allocator,+    VmaDefragmentationContext context,+    VmaDefragmentationStats* pStats)+{+    VMA_ASSERT(allocator && context);++    VMA_DEBUG_LOG("vmaEndDefragmentation");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    if (pStats)+        context->GetStats(*pStats);+    vma_delete(allocator, context);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaDefragmentationContext VMA_NOT_NULL context,+    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)+{+    VMA_ASSERT(context && pPassInfo);++    VMA_DEBUG_LOG("vmaBeginDefragmentationPass");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return context->DefragmentPassBegin(*pPassInfo);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaDefragmentationContext VMA_NOT_NULL context,+    VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo)+{+    VMA_ASSERT(context && pPassInfo);++    VMA_DEBUG_LOG("vmaEndDefragmentationPass");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return context->DefragmentPassEnd(*pPassInfo);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkBuffer buffer)+{+    VMA_ASSERT(allocator && allocation && buffer);++    VMA_DEBUG_LOG("vmaBindBufferMemory");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkDeviceSize allocationLocalOffset,+    VkBuffer buffer,+    const void* pNext)+{+    VMA_ASSERT(allocator && allocation && buffer);++    VMA_DEBUG_LOG("vmaBindBufferMemory2");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkImage image)+{+    VMA_ASSERT(allocator && allocation && image);++    VMA_DEBUG_LOG("vmaBindImageMemory");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    return allocator->BindImageMemory(allocation, 0, image, VMA_NULL);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2(+    VmaAllocator allocator,+    VmaAllocation allocation,+    VkDeviceSize allocationLocalOffset,+    VkImage image,+    const void* pNext)+{+    VMA_ASSERT(allocator && allocation && image);++    VMA_DEBUG_LOG("vmaBindImageMemory2");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++        return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer(+    VmaAllocator allocator,+    const VkBufferCreateInfo* pBufferCreateInfo,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    VkBuffer* pBuffer,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation);++    if(pBufferCreateInfo->size == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }+    if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&+        !allocator->m_UseKhrBufferDeviceAddress)+    {+        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VMA_DEBUG_LOG("vmaCreateBuffer");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    *pBuffer = VK_NULL_HANDLE;+    *pAllocation = VK_NULL_HANDLE;++    // 1. Create VkBuffer.+    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(+        allocator->m_hDevice,+        pBufferCreateInfo,+        allocator->GetAllocationCallbacks(),+        pBuffer);+    if(res >= 0)+    {+        // 2. vkGetBufferMemoryRequirements.+        VkMemoryRequirements vkMemReq = {};+        bool requiresDedicatedAllocation = false;+        bool prefersDedicatedAllocation  = false;+        allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,+            requiresDedicatedAllocation, prefersDedicatedAllocation);++        // 3. Allocate memory using allocator.+        res = allocator->AllocateMemory(+            vkMemReq,+            requiresDedicatedAllocation,+            prefersDedicatedAllocation,+            *pBuffer, // dedicatedBuffer+            VK_NULL_HANDLE, // dedicatedImage+            VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage+            *pAllocationCreateInfo,+            VMA_SUBALLOCATION_TYPE_BUFFER,+            1, // allocationCount+            pAllocation);++        if(res >= 0)+        {+            // 3. Bind buffer with memory.+            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)+            {+                res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);+            }+            if(res >= 0)+            {+                // All steps succeeded.+                #if VMA_STATS_STRING_ENABLED+                    (*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5);+                #endif+                if(pAllocationInfo != VMA_NULL)+                {+                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+                }++                return VK_SUCCESS;+            }+            allocator->FreeMemory(+                1, // allocationCount+                pAllocation);+            *pAllocation = VK_NULL_HANDLE;+            (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());+            *pBuffer = VK_NULL_HANDLE;+            return res;+        }+        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());+        *pBuffer = VK_NULL_HANDLE;+        return res;+    }+    return res;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment(+    VmaAllocator allocator,+    const VkBufferCreateInfo* pBufferCreateInfo,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    VkDeviceSize minAlignment,+    VkBuffer* pBuffer,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation);++    if(pBufferCreateInfo->size == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }+    if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&+        !allocator->m_UseKhrBufferDeviceAddress)+    {+        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VMA_DEBUG_LOG("vmaCreateBufferWithAlignment");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    *pBuffer = VK_NULL_HANDLE;+    *pAllocation = VK_NULL_HANDLE;++    // 1. Create VkBuffer.+    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(+        allocator->m_hDevice,+        pBufferCreateInfo,+        allocator->GetAllocationCallbacks(),+        pBuffer);+    if(res >= 0)+    {+        // 2. vkGetBufferMemoryRequirements.+        VkMemoryRequirements vkMemReq = {};+        bool requiresDedicatedAllocation = false;+        bool prefersDedicatedAllocation  = false;+        allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq,+            requiresDedicatedAllocation, prefersDedicatedAllocation);++        // 2a. Include minAlignment+        vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment);++        // 3. Allocate memory using allocator.+        res = allocator->AllocateMemory(+            vkMemReq,+            requiresDedicatedAllocation,+            prefersDedicatedAllocation,+            *pBuffer, // dedicatedBuffer+            VK_NULL_HANDLE, // dedicatedImage+            VmaBufferImageUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5), // dedicatedBufferImageUsage+            *pAllocationCreateInfo,+            VMA_SUBALLOCATION_TYPE_BUFFER,+            1, // allocationCount+            pAllocation);++        if(res >= 0)+        {+            // 3. Bind buffer with memory.+            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)+            {+                res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL);+            }+            if(res >= 0)+            {+                // All steps succeeded.+                #if VMA_STATS_STRING_ENABLED+                    (*pAllocation)->InitBufferUsage(*pBufferCreateInfo, allocator->m_UseKhrMaintenance5);+                #endif+                if(pAllocationInfo != VMA_NULL)+                {+                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+                }++                return VK_SUCCESS;+            }+            allocator->FreeMemory(+                1, // allocationCount+                pAllocation);+            *pAllocation = VK_NULL_HANDLE;+            (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());+            *pBuffer = VK_NULL_HANDLE;+            return res;+        }+        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());+        *pBuffer = VK_NULL_HANDLE;+        return res;+    }+    return res;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)+{+    return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo,+    VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer)+{+    VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation);+    VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize());++    VMA_DEBUG_LOG("vmaCreateAliasingBuffer2");++    *pBuffer = VK_NULL_HANDLE;++    if (pBufferCreateInfo->size == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }+    if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 &&+        !allocator->m_UseKhrBufferDeviceAddress)+    {+        VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used.");+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    // 1. Create VkBuffer.+    VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)(+        allocator->m_hDevice,+        pBufferCreateInfo,+        allocator->GetAllocationCallbacks(),+        pBuffer);+    if (res >= 0)+    {+        // 2. Bind buffer with memory.+        res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL);+        if (res >= 0)+        {+            return VK_SUCCESS;+        }+        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());+    }+    return res;+}++VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer(+    VmaAllocator allocator,+    VkBuffer buffer,+    VmaAllocation allocation)+{+    VMA_ASSERT(allocator);++    if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)+    {+        return;+    }++    VMA_DEBUG_LOG("vmaDestroyBuffer");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    if(buffer != VK_NULL_HANDLE)+    {+        (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());+    }++    if(allocation != VK_NULL_HANDLE)+    {+        allocator->FreeMemory(+            1, // allocationCount+            &allocation);+    }+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage(+    VmaAllocator allocator,+    const VkImageCreateInfo* pImageCreateInfo,+    const VmaAllocationCreateInfo* pAllocationCreateInfo,+    VkImage* pImage,+    VmaAllocation* pAllocation,+    VmaAllocationInfo* pAllocationInfo)+{+    VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation);++    VMA_ASSERT((pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 &&+        "vmaCreateImage() doesn't support disjoint multi-planar images. Please allocate memory for the planes using vmaAllocateMemory() and bind them using vmaBindImageMemory2().");++    if(pImageCreateInfo->extent.width == 0 ||+        pImageCreateInfo->extent.height == 0 ||+        pImageCreateInfo->extent.depth == 0 ||+        pImageCreateInfo->mipLevels == 0 ||+        pImageCreateInfo->arrayLayers == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VMA_DEBUG_LOG("vmaCreateImage");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    *pImage = VK_NULL_HANDLE;+    *pAllocation = VK_NULL_HANDLE;++    // 1. Create VkImage.+    VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(+        allocator->m_hDevice,+        pImageCreateInfo,+        allocator->GetAllocationCallbacks(),+        pImage);+    if(res == VK_SUCCESS)+    {+        VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?+            VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :+            VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;++        // 2. Allocate memory using allocator.+        VkMemoryRequirements vkMemReq = {};+        bool requiresDedicatedAllocation = false;+        bool prefersDedicatedAllocation  = false;+        allocator->GetImageMemoryRequirements(*pImage, vkMemReq,+            requiresDedicatedAllocation, prefersDedicatedAllocation);++        res = allocator->AllocateMemory(+            vkMemReq,+            requiresDedicatedAllocation,+            prefersDedicatedAllocation,+            VK_NULL_HANDLE, // dedicatedBuffer+            *pImage, // dedicatedImage+            VmaBufferImageUsage(*pImageCreateInfo), // dedicatedBufferImageUsage+            *pAllocationCreateInfo,+            suballocType,+            1, // allocationCount+            pAllocation);++        if(res == VK_SUCCESS)+        {+            // 3. Bind image with memory.+            if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0)+            {+                res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL);+            }+            if(res == VK_SUCCESS)+            {+                // All steps succeeded.+                #if VMA_STATS_STRING_ENABLED+                    (*pAllocation)->InitImageUsage(*pImageCreateInfo);+                #endif+                if(pAllocationInfo != VMA_NULL)+                {+                    allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);+                }++                return VK_SUCCESS;+            }+            allocator->FreeMemory(+                1, // allocationCount+                pAllocation);+            *pAllocation = VK_NULL_HANDLE;+            (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());+            *pImage = VK_NULL_HANDLE;+            return res;+        }+        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());+        *pImage = VK_NULL_HANDLE;+        return res;+    }+    return res;+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)+{+    return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2(+    VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation,+    VkDeviceSize allocationLocalOffset,+    const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage)+{+    VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation);++    *pImage = VK_NULL_HANDLE;++    VMA_DEBUG_LOG("vmaCreateImage2");++    if (pImageCreateInfo->extent.width == 0 ||+        pImageCreateInfo->extent.height == 0 ||+        pImageCreateInfo->extent.depth == 0 ||+        pImageCreateInfo->mipLevels == 0 ||+        pImageCreateInfo->arrayLayers == 0)+    {+        return VK_ERROR_INITIALIZATION_FAILED;+    }++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    // 1. Create VkImage.+    VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)(+        allocator->m_hDevice,+        pImageCreateInfo,+        allocator->GetAllocationCallbacks(),+        pImage);+    if (res >= 0)+    {+        // 2. Bind image with memory.+        res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL);+        if (res >= 0)+        {+            return VK_SUCCESS;+        }+        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());+    }+    return res;+}++VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage(+    VmaAllocator VMA_NOT_NULL allocator,+    VkImage VMA_NULLABLE_NON_DISPATCHABLE image,+    VmaAllocation VMA_NULLABLE allocation)+{+    VMA_ASSERT(allocator);++    if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE)+    {+        return;+    }++    VMA_DEBUG_LOG("vmaDestroyImage");++    VMA_DEBUG_GLOBAL_MUTEX_LOCK++    if(image != VK_NULL_HANDLE)+    {+        (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());+    }+    if(allocation != VK_NULL_HANDLE)+    {+        allocator->FreeMemory(+            1, // allocationCount+            &allocation);+    }+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock(+    const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo,+    VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock)+{+    VMA_ASSERT(pCreateInfo && pVirtualBlock);+    VMA_ASSERT(pCreateInfo->size > 0);+    VMA_DEBUG_LOG("vmaCreateVirtualBlock");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo);+    return VK_SUCCESS;++    /*+    Code for the future if we ever need a separate Init() method that could fail:++    VkResult res = (*pVirtualBlock)->Init();+    if(res < 0)+    {+        vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock);+        *pVirtualBlock = VK_NULL_HANDLE;+    }+    return res;+    */+}++VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock)+{+    if(virtualBlock != VK_NULL_HANDLE)+    {+        VMA_DEBUG_LOG("vmaDestroyVirtualBlock");+        VMA_DEBUG_GLOBAL_MUTEX_LOCK;+        VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying.+        vma_delete(&allocationCallbacks, virtualBlock);+    }+}++VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);+    VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE;+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL);+    VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo);+}++VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation,+    VkDeviceSize* VMA_NULLABLE pOffset)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL);+    VMA_DEBUG_LOG("vmaVirtualAllocate");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset);+}++VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation)+{+    if(allocation != VK_NULL_HANDLE)+    {+        VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);+        VMA_DEBUG_LOG("vmaVirtualFree");+        VMA_DEBUG_GLOBAL_MUTEX_LOCK;+        virtualBlock->Free(allocation);+    }+}++VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);+    VMA_DEBUG_LOG("vmaClearVirtualBlock");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    virtualBlock->Clear();+}++VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);+    VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    virtualBlock->SetAllocationUserData(allocation, pUserData);+}++VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaStatistics* VMA_NOT_NULL pStats)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);+    VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    virtualBlock->GetStatistics(*pStats);+}++VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    VmaDetailedStatistics* VMA_NOT_NULL pStats)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL);+    VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics");+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    virtualBlock->CalculateDetailedStatistics(*pStats);+}++#if VMA_STATS_STRING_ENABLED++VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap)+{+    VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL);+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks();+    VmaStringBuilder sb(allocationCallbacks);+    virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb);+    *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength());+}++VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock,+    char* VMA_NULLABLE pStatsString)+{+    if(pStatsString != VMA_NULL)+    {+        VMA_ASSERT(virtualBlock != VK_NULL_HANDLE);+        VMA_DEBUG_GLOBAL_MUTEX_LOCK;+        VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString);+    }+}+#if VMA_EXTERNAL_MEMORY_WIN32+VMA_CALL_PRE VkResult VMA_CALL_POST vmaGetMemoryWin32Handle(VmaAllocator VMA_NOT_NULL allocator,+    VmaAllocation VMA_NOT_NULL allocation, HANDLE hTargetProcess, HANDLE* VMA_NOT_NULL pHandle)+{+    VMA_ASSERT(allocator && allocation && pHandle);+    VMA_DEBUG_GLOBAL_MUTEX_LOCK;+    return allocation->GetWin32Handle(allocator, hTargetProcess, pHandle);+}+#endif // VMA_EXTERNAL_MEMORY_WIN32 +#endif // VMA_STATS_STRING_ENABLED+#endif // _VMA_PUBLIC_INTERFACE+#endif // VMA_IMPLEMENTATION++/**+\page faq Frequenty asked questions++<b>What is VMA?</b>++Vulkan(R) Memory Allocator (VMA) is a software library for developers who use the Vulkan graphics API in their code.+It is written in C++.++<b>What is the license of VMA?</b>++VMA is licensed under MIT, which means it is open source and free software.++<b>What is the purpose of VMA?</b>++VMA helps with handling one aspect of Vulkan usage, which is device memory management -+allocation of `VkDeviceMemory` objects, and creation of `VkBuffer` and `VkImage` objects.++<b>Do I need to use VMA?</b>++You don't need to, but it may be beneficial in many cases.+Vulkan is a complex and low-level API, so libraries like this that abstract certain aspects of the API+and bring them to a higher level are useful.+When developing any non-trivial Vulkan application, you likely need to use a memory allocator.+Using VMA can save time compared to implementing your own.++<b>When should I not use VMA?</b>++While VMA is useful for most applications that use the Vulkan API, there are cases+when it may be a better choice not to use it.+For example, if the application is very simple, e.g. serving as a sample or a learning exercise+to help you understand or teach others the basics of Vulkan,+and it creates only a small number of buffers and images, then including VMA may be an overkill.+Developing your own memory allocator may also be a good learning exercise.++<b>What are the benefits of using VMA?</b>++-# VMA helps in choosing the optimal memory type for your resource (buffer or image).+   In Vulkan, we have a two-level hierarchy of memory heaps and types with different flags,+   and each device can expose a different set of those.+   Implementing logic that would select the best memory type on each platform is a non-trivial task.+   VMA does that, expecting only a high-level description of the intended usage of your resource.+   For more information, see \subpage choosing_memory_type.+-# VMA allocates large blocks of `VkDeviceMemory` and sub-allocates parts of them for your resources.+   Allocating a new block of device memory may be a time-consuming operation.+   Some platforms also have a limit on the maximum number of those blocks (`VkPhysicalDeviceLimits::maxMemoryAllocationCount`)+   as low as 4096, so allocating a separate one for each resource is not an option.+   Sub-allocating parts of a memory block requires implementing an allocation algorithm,+   which is a non-trivial task.+   VMA does that, using an advanced and efficient algorithm that works well in various use cases.+-# VMA offers a simple API that allows creating buffers and textures within one function call.+   In Vulkan, the creation of a resource is a multi-step process.+   You need to create a `VkBuffer` or `VkImage`, ask it for memory requirements,+   allocate a `VkDeviceMemory` object, and finally bind the resource to the memory block.+   VMA does that automatically under a simple API within one function call: vmaCreateBuffer(), vmaCreateImage().++The library is doing much more under the hood.+For example, it respects limits like `bufferImageGranularity`, `nonCoherentAtomSize`,+and `VkMemoryDedicatedRequirements` automatically, so you don't need to think about it.++<b>Which version should I pick?</b>++You can just pick [the latest version from the "master" branch](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator).+It is kept in a good shape most of the time, compiling and working correctly,+with no compatibility-breaking changes and no unfinished code.++If you want an even more stable version, you can pick+[the latest official release](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/releases).+Current code from the master branch is occasionally tagged as a release,+with [CHANGELOG](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/blob/master/CHANGELOG.md)+carefully curated to enumerate all important changes since the previous version.++The library uses [Semantic Versioning](https://semver.org/),+which means versions that only differ in the patch number are forward and backward compatible+(e.g., only fixing some bugs), while versions that differ in the minor number are backward compatible+(e.g., only adding new functions to the API, but not removing or changing existing ones).++<b>How to integrate it with my code?</b>++VMA is an STB-style single-header C++ library.++You can pull the entire GitHub repository, e.g. using Git submodules.+The repository contains ancillary files like the Cmake script, Doxygen config file,+sample application, test suite, and others.+You can compile it as a library and link with your project.++However, a simpler way is taking the single file "include/vk_mem_alloc.h" and including it in your project.+This extensive file contains all you need: a copyright notice,+declarations of the public library interface (API), its internal implementation,+and even the documentation in form of Doxygen-style comments.++The "STB style" means not everything is implemented as inline functions in the header file.+You need to extract the internal implementation using a special macro.+This means that in every .cpp file where you need to use the library you should+`#include "vk_mem_alloc.h"` to include its public interface,+but additionally in exactly one .cpp file you should `#define VMA_IMPLEMENTATION`+before this `#include` to enable its internal implementation.+For more information, see [Project setup](@ref quick_start_project_setup).++<b>Does the library work with C or C++?</b>++The internal implementation of VMA is written in C++.+It is distributed in the source format, so you need a compiler supporting at least C++14 to build it.++However, the public interface of the library is written in C - using only enums, structs, and global functions,+in the same style as Vulkan, so you can use the library in the C code.++<b>I am not a fan of modern C++. Can I still use it?</b>++Very likely yes.+We acknowledge that many C++ developers, especially in the games industry,+do not appreciate all the latest features that the language has to offer.++- VMA doesn't throw or catch any C++ exceptions.+  It reports errors by returning a `VkResult` value instead, just like Vulkan.+  If you don't use exceptions in your project, your code is not exception-safe,+  or even if you disable exception handling in the compiler options, you can still use VMA.+- VMA doesn't use C++ run-time type information like `typeid` or `dynamic_cast`,+  so if you disable RTTI in the compiler options, you can still use the library.+- VMA uses only a limited subset of standard C and C++ library.+  It doesn't use STL containers like `std::vector`, `map`, or `string`,+  either in the public interface nor in the internal implementation.+  It implements its own containers instead.+- If you don't use the default heap memory allocator through `malloc/free` or `new/delete`+  but implement your own allocator instead, you can pass it to VMA and+  the library will use your functions for every dynamic heap allocation made internally,+  as well as passing it further to Vulkan functions. For details, see [Custom host memory allocator](@ref custom_memory_allocator).++<b>Is it available for other programming languages?</b>++VMA is a C++ library with C interface in similar style as Vulkan.+An object-oriented C++ wrapper or bindings to other programming languages are out of scope of this project,+but they are welcome as external projects.+Some of them are listed in [README.md, "See also" section](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator?tab=readme-ov-file#see-also),+including binding to C++, Python, Rust, and Haskell.+Before using any of them, please check if they are still maintained and updated to use a recent version of VMA.++<b>What platforms does it support?</b>++VMA relies only on Vulkan and some parts of the standard C and C++ library,+so it supports any platform where a C++ compiler and Vulkan are available.+It is developed mostly on Microsoft(R) Windows(R),+but it has been successfully used in Linux(R), MacOS, Android, and even FreeBSD and Raspberry Pi.++<b>Does it only work on AMD GPUs?</b>++No! While VMA is published by AMD, it works on any GPU that supports Vulkan,+whether a discrete PC graphics card, a processor integrated graphics, or a mobile SoC.+It doesn't give AMD GPUs any advantage over any other GPUs.++<b>What Vulkan versions and extensions are supported?</b>++VMA is updated to support the latest versions of Vulkan.+It currently supports Vulkan up to 1.4.+The library also supports older versions down to the first release of Vulkan 1.0.+Defining a higher minimum version support would help simplify the code,+but we acknowledge that developers on some platforms like Android still use older versions,+so the support is provided for all of them.++Among many extensions available for Vulkan, only a few interact with memory management.+VMA can automatically take advantage of them. Some of them are:+VK_EXT_memory_budget, VK_EXT_memory_priority, VK_KHR_external_memory_win32, and VK_KHR_maintenance*+extensions that are later promoted to the new versions of the core Vulkan API.++To use them, it is your responsibility to validate if they are available on the current system and if so,+enable them while creating the Vulkan device object.+You also need to pass appropriate #VmaAllocatorCreateFlagBits to inform VMA that they are enabled.+Then, the library will automatically take advantage of them.+For more information and the full list of supported extensions, see [Enabling extensions](@ref quick_start_initialization_enabling_extensions).++<b>Does it support other graphics APIs, like Microsoft DirectX(R) 12?</b>++No, but we offer an equivalent library for DirectX 12:+[D3D12 Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator).+It uses the same core allocation algorithm.+It also shares many features with VMA, like the support for custom pools and virtual allocator.+However, it is not identical in terms of the features supported.+Its API also looks different, because while the interface of VMA is similar in style to Vulkan,+the interface of D3D12MA is similar to DirectX 12.++<b>Is the library lightweight?</b>++It depends on how you define it.+VMA is implemented with high-performance and real-time applications like video games in mind.+The CPU performance overhead of using this library is low.+It uses a high-quality allocation algorithm called Two-Level Segregated Fit (TLSF),+which in most cases can find a free place for a new allocation in few steps.+The library also doesn't perform too many CPU heap allocations.+In many cases, the allocation happens with 0 new CPU heap allocations performed by the library.+Even the creation of a #VmaAllocation object doesn't typically feature an CPU allocation,+because these objects are returned out of a dedicated memory pool.++On the other hand, however, VMA needs some extra memory and extra time+to maintain the metadata about the occupied and free regions of the memory blocks,+and the algorithms and data structures used must be generic enough to work well in most cases.+If you develop your program for a very resource-constrained platform,+a custom allocator simpler than VMA may be a better choice.++<b>Does it have a documentation?</b>++Yes! VMA comes with full documentation of all elements of the API (functions, structures, enums),+as well as many generic chapters that provide an introduction,+describe core concepts of the library, good practices, etc.+The entire documentation is written in form of code comments inside "vk_mem_alloc.h", in Doxygen format.+You can access it in multiple ways:++- Browsable online: https://gpuopen-librariesandsdks.github.io/VulkanMemoryAllocator/html/+- Local HTML pages available after you clone the repository and open file "docs/html/index.html".+- You can rebuild the documentation in HTML or some other format from the source code using Doxygen.+  Configuration file "Doxyfile" is part of the repository.+- Finally, you can just read the comments preceding declarations of any public functions of the library.++<b>Is it a mature project?</b>++Yes! The library is in development since June 2017, has over 1000 commits, over 400 issue tickets+and pull requests (most of them resolved), and over 70 contributors.+It is distributed together with Vulkan SDK.+It is used by many software projects, including some large and popular ones like Qt or Blender,+as well as some AAA games.+According to the [LunarG 2024 Ecosystem Survey](https://www.lunarg.com/2024-ecosystem-survey-progress-report-released/),+it is used by over 50% of Vulkan developers.++<b>How can I contribute to the project?</b>++If you have an idea for improvement or a feature request,+you can go to [the library repository](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)+and create an Issue ticket, describing your idea.+You can also implement it yourself by forking the repository, making changes to the code,+and creating a Pull request.++If you want to ask a question, you can also create a ticket the same way.+Before doing this, please make sure you read the relevant part of the Vulkan specification and VMA documentation,+where you may find the answers to your question.++If you want to report a suspected bug, you can also create a ticket the same way.+Before doing this, please put some effort into the investigation of whether the bug is really+in the library and not in your code or in the Vulkan implementation (the GPU driver) on your platform:++- Enable Vulkan validation layer and make sure it is free from any errors.+- Make sure `VMA_ASSERT` is defined to an implementation that can report a failure and not ignore it.+- Try making your allocation using pure Vulkan functions rather than VMA and see if the bug persists.++<b>I found some compilation warnings. How can we fix them?</b>++Seeing compiler warnings may be annoying to some developers,+but it is a design decision to not fix all of them.+Due to the nature of the C++ language, certain preprocessor macros can make some variables unused,+function parameters unreferenced, or conditional expressions constant in some configurations.+The code of this library should not be bigger or more complicated just to silence these warnings.+It is recommended to disable such warnings instead.+For more information, see [Features not supported](@ref general_considerations_features_not_supported).++However, if you observe a warning that is really dangerous, e.g.,+about an implicit conversion from a larger to a smaller integer type, please report it and it will be fixed ASAP.+++\page quick_start Quick start++\section quick_start_project_setup Project setup++Vulkan Memory Allocator comes in form of a "stb-style" single header file.+While you can pull the entire repository e.g. as Git module, there is also Cmake script provided,+you don't need to build it as a separate library project.+You can add file "vk_mem_alloc.h" directly to your project and submit it to code repository next to your other source files.++"Single header" doesn't mean that everything is contained in C/C++ declarations,+like it tends to be in case of inline functions or C++ templates.+It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro.+If you don't do it properly, it will result in linker errors.++To do it properly:++-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library.+   This includes declarations of all members of the library.+-# In exactly one CPP file define following macro before this include.+   It enables also internal definitions.++\code+#define VMA_IMPLEMENTATION+#include "vk_mem_alloc.h"+\endcode++It may be a good idea to create dedicated CPP file just for this purpose, e.g. "VmaUsage.cpp".++This library includes header `<vulkan/vulkan.h>`, which in turn+includes `<windows.h>` on Windows. If you need some specific macros defined+before including these headers (like `WIN32_LEAN_AND_MEAN` or+`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define+them before every `#include` of this library.+It may be a good idea to create a dedicate header file for this purpose, e.g. "VmaUsage.h",+that will be included in other source files instead of VMA header directly.++This library is written in C++, but has C-compatible interface.+Thus, you can include and use "vk_mem_alloc.h" in C or C++ code, but full+implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C.+Some features of C++14 are used and required. Features of C++20 are used optionally when available.+Some headers of standard C and C++ library are used, but STL containers, RTTI, or C++ exceptions are not used.+++\section quick_start_initialization Initialization++VMA offers library interface in a style similar to Vulkan, with object handles like #VmaAllocation,+structures describing parameters of objects to be created like #VmaAllocationCreateInfo,+and errors codes returned from functions using `VkResult` type.++The first and the main object that needs to be created is #VmaAllocator.+It represents the initialization of the entire library.+Only one such object should be created per `VkDevice`.+You should create it at program startup, after `VkDevice` was created, and before any device memory allocator needs to be made.+It must be destroyed before `VkDevice` is destroyed.++At program startup:++-# Initialize Vulkan to have `VkInstance`, `VkPhysicalDevice`, `VkDevice` object.+-# Fill VmaAllocatorCreateInfo structure and call vmaCreateAllocator() to create #VmaAllocator object.++Only members `physicalDevice`, `device`, `instance` are required.+However, you should inform the library which Vulkan version do you use by setting+VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable+by setting VmaAllocatorCreateInfo::flags.+Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions.+See below for details.++\subsection quick_start_initialization_selecting_vulkan_version Selecting Vulkan version++VMA supports Vulkan version down to 1.0, for backward compatibility.+If you want to use higher version, you need to inform the library about it.+This is a two-step process.++<b>Step 1: Compile time.</b> By default, VMA compiles with code supporting the highest+Vulkan version found in the included `<vulkan/vulkan.h>` that is also supported by the library.+If this is OK, you don't need to do anything.+However, if you want to compile VMA as if only some lower Vulkan version was available,+define macro `VMA_VULKAN_VERSION` before every `#include "vk_mem_alloc.h"`.+It should have decimal numeric value in form of ABBBCCC, where A = major, BBB = minor, CCC = patch Vulkan version.+For example, to compile against Vulkan 1.2:++\code+#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2+#include "vk_mem_alloc.h"+\endcode++<b>Step 2: Runtime.</b> Even when compiled with higher Vulkan version available,+VMA can use only features of a lower version, which is configurable during creation of the #VmaAllocator object.+By default, only Vulkan 1.0 is used.+To initialize the allocator with support for higher Vulkan version, you need to set member+VmaAllocatorCreateInfo::vulkanApiVersion to an appropriate value, e.g. using constants like `VK_API_VERSION_1_2`.+See code sample below.++\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions++You may need to configure importing Vulkan functions. There are 4 ways to do this:++-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows):+   - You don't need to do anything.+   - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default.+-# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`,+   `vkGetDeviceProcAddr` (this is the option presented in the example below):+   - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1.+   - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr,+     VmaVulkanFunctions::vkGetDeviceProcAddr.+   - The library will fetch pointers to all other functions it needs internally.+-# **If you fetch pointers to all Vulkan functions in a custom way**:+   - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.+   - Pass these pointers via structure #VmaVulkanFunctions.+-# **If you use [volk library](https://github.com/zeux/volk)**:+   - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0.+   - Use function vmaImportVulkanFunctionsFromVolk() to fill in the structure #VmaVulkanFunctions.+     For more information, see the description of this function.++\subsection quick_start_initialization_enabling_extensions Enabling extensions++VMA can automatically use following Vulkan extensions.+If you found them available on the selected physical device and you enabled them+while creating `VkInstance` / `VkDevice` object, inform VMA about their availability+by setting appropriate flags in VmaAllocatorCreateInfo::flags.++Vulkan extension              | VMA flag+------------------------------|-----------------------------------------------------+VK_KHR_dedicated_allocation   | #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT+VK_KHR_bind_memory2           | #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT+VK_KHR_maintenance4           | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE4_BIT+VK_KHR_maintenance5           | #VMA_ALLOCATOR_CREATE_KHR_MAINTENANCE5_BIT+VK_EXT_memory_budget          | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT+VK_KHR_buffer_device_address  | #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT+VK_EXT_memory_priority        | #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT+VK_AMD_device_coherent_memory | #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT+VK_KHR_external_memory_win32  | #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT++Example with fetching pointers to Vulkan functions dynamically:++\code+#define VMA_STATIC_VULKAN_FUNCTIONS 0+#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1+#include "vk_mem_alloc.h"++...++VmaVulkanFunctions vulkanFunctions = {};+vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr;+vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr;++VmaAllocatorCreateInfo allocatorCreateInfo = {};+allocatorCreateInfo.flags = VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;+allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2;+allocatorCreateInfo.physicalDevice = physicalDevice;+allocatorCreateInfo.device = device;+allocatorCreateInfo.instance = instance;+allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;++VmaAllocator allocator;+vmaCreateAllocator(&allocatorCreateInfo, &allocator);++// Entire program...++// At the end, don't forget to:+vmaDestroyAllocator(allocator);+\endcode+++\subsection quick_start_initialization_other_config Other configuration options++There are additional configuration options available through preprocessor macros that you can define+before including VMA header and through parameters passed in #VmaAllocatorCreateInfo.+They include a possibility to use your own callbacks for host memory allocations (`VkAllocationCallbacks`),+callbacks for device memory allocations (instead of `vkAllocateMemory`, `vkFreeMemory`),+or your custom `VMA_ASSERT` macro, among others.+For more information, see: @ref configuration.+++\section quick_start_resource_allocation Resource allocation++When you want to create a buffer or image:++-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure.+-# Fill VmaAllocationCreateInfo structure.+-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory+   already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory.++\code+VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufferInfo.size = 65536;+bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocInfo = {};+allocInfo.usage = VMA_MEMORY_USAGE_AUTO;++VkBuffer buffer;+VmaAllocation allocation;+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);+\endcode++Don't forget to destroy your buffer and allocation objects when no longer needed:++\code+vmaDestroyBuffer(allocator, buffer, allocation);+\endcode++If you need to map the buffer, you must set flag+#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT+in VmaAllocationCreateInfo::flags.+There are many additional parameters that can control the choice of memory type to be used for the allocation+and other features.+For more information, see documentation chapters: @ref choosing_memory_type, @ref memory_mapping.+++\page choosing_memory_type Choosing memory type++Physical devices in Vulkan support various combinations of memory heaps and+types. Help with choosing correct and optimal memory type for your specific+resource is one of the key features of this library. You can use it by filling+appropriate members of VmaAllocationCreateInfo structure, as described below.+You can also combine multiple methods.++-# If you just want to find memory type index that meets your requirements, you+   can use function: vmaFindMemoryTypeIndexForBufferInfo(),+   vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex().+-# If you want to allocate a region of device memory without association with any+   specific image or buffer, you can use function vmaAllocateMemory(). Usage of+   this function is not recommended and usually not needed.+   vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once,+   which may be useful for sparse binding.+-# If you already have a buffer or an image created, you want to allocate memory+   for it and then you will bind it yourself, you can use function+   vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage().+   For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory()+   or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2().+-# If you want to create a buffer or an image, allocate memory for it, and bind+   them together, all in one call, you can use function vmaCreateBuffer(),+   vmaCreateImage().+   <b>This is the easiest and recommended way to use this library!</b>++When using 3. or 4., the library internally queries Vulkan for memory types+supported for that buffer or image (function `vkGetBufferMemoryRequirements()`)+and uses only one of these types.++If no memory type can be found that meets all the requirements, these functions+return `VK_ERROR_FEATURE_NOT_PRESENT`.++You can leave VmaAllocationCreateInfo structure completely filled with zeros.+It means no requirements are specified for memory type.+It is valid, although not very useful.++\section choosing_memory_type_usage Usage++The easiest way to specify memory requirements is to fill member+VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage.+It defines high level, common usage types.+Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically.++For example, if you want to create a uniform buffer that will be filled using+transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can+do it using following code. The buffer will most likely end up in a memory type with+`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device.++\code+VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufferInfo.size = 65536;+bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocInfo = {};+allocInfo.usage = VMA_MEMORY_USAGE_AUTO;++VkBuffer buffer;+VmaAllocation allocation;+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);+\endcode++If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory+on systems with discrete graphics card that have the memories separate, you can use+#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST.++When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory,+you also need to specify one of the host access flags:+#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.+This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`+so you can map it.++For example, a staging buffer that will be filled via mapped pointer and then+used as a source of transfer to the buffer described previously can be created like this.+It will likely end up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT`+but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM).++\code+VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+stagingBufferInfo.size = 65536;+stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;++VmaAllocationCreateInfo stagingAllocInfo = {};+stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO;+stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;++VkBuffer stagingBuffer;+VmaAllocation stagingAllocation;+vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr);+\endcode++For more examples of creating different kinds of resources, see chapter \ref usage_patterns.+See also: @ref memory_mapping.++Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows+about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed,+so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc.+If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting+memory type, as described below.++\note+Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`,+`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`)+are still available and work same way as in previous versions of the library+for backward compatibility, but they are deprecated.++\section choosing_memory_type_required_preferred_flags Required and preferred flags++You can specify more detailed requirements by filling members+VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags+with a combination of bits from enum `VkMemoryPropertyFlags`. For example,+if you want to create a buffer that will be persistently mapped on host (so it+must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`,+use following code:++\code+VmaAllocationCreateInfo allocInfo = {};+allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;+allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;+allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT;++VkBuffer buffer;+VmaAllocation allocation;+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);+\endcode++A memory type is chosen that has all the required flags and as many preferred+flags set as possible.++Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags,+plus some extra "magic" (heuristics).++\section choosing_memory_type_explicit_memory_types Explicit memory types++If you inspected memory types available on the physical device and <b>you have+a preference for memory types that you want to use</b>, you can fill member+VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set+means that a memory type with that index is allowed to be used for the+allocation. Special value 0, just like `UINT32_MAX`, means there are no+restrictions to memory type index.++Please note that this member is NOT just a memory type index.+Still you can use it to choose just one, specific memory type.+For example, if you already determined that your buffer should be created in+memory type 2, use following code:++\code+uint32_t memoryTypeIndex = 2;++VmaAllocationCreateInfo allocInfo = {};+allocInfo.memoryTypeBits = 1U << memoryTypeIndex;++VkBuffer buffer;+VmaAllocation allocation;+vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr);+\endcode++You can also use this parameter to <b>exclude some memory types</b>.+If you inspect memory heaps and types available on the current physical device and+you determine that for some reason you don't want to use a specific memory type for the allocation,+you can enable automatic memory type selection but exclude certain memory type or types+by setting all bits of `memoryTypeBits` to 1 except the ones you choose.++\code+// ...+uint32_t excludedMemoryTypeIndex = 2;+VmaAllocationCreateInfo allocInfo = {};+allocInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocInfo.memoryTypeBits = ~(1U << excludedMemoryTypeIndex);+// ...+\endcode+++\section choosing_memory_type_custom_memory_pools Custom memory pools++If you allocate from custom memory pool, all the ways of specifying memory+requirements described above are not applicable and the aforementioned members+of VmaAllocationCreateInfo structure are ignored. Memory type is selected+explicitly when creating the pool and then used to make all the allocations from+that pool. For further details, see \ref custom_memory_pools.++\section choosing_memory_type_dedicated_allocations Dedicated allocations++Memory for allocations is reserved out of larger block of `VkDeviceMemory`+allocated from Vulkan internally. That is the main feature of this whole library.+You can still request a separate memory block to be created for an allocation,+just like you would do in a trivial solution without using any allocator.+In that case, a buffer or image is always bound to that memory at offset 0.+This is called a "dedicated allocation".+You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.+The library can also internally decide to use dedicated allocation in some cases, e.g.:++- When the size of the allocation is large.+- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled+  and it reports that dedicated allocation is required or recommended for the resource.+- When allocation of next big memory block fails due to not enough device memory,+  but allocation with the exact requested size succeeds.+++\page memory_mapping Memory mapping++To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`,+to be able to read from it or write to it in CPU code.+Mapping is possible only of memory allocated from a memory type that has+`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.+Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose.+You can use them directly with memory allocated by this library,+but it is not recommended because of following issue:+Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.+This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.+It is also not thread-safe.+Because of this, Vulkan Memory Allocator provides following facilities:++\note If you want to be able to map an allocation, you need to specify one of the flags+#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT+in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable+when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.+For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,+but these flags can still be used for consistency.++\section memory_mapping_copy_functions Copy functions++The easiest way to copy data from a host pointer to an allocation is to use convenience function vmaCopyMemoryToAllocation().+It automatically maps the Vulkan memory temporarily (if not already mapped), performs `memcpy`,+and calls `vkFlushMappedMemoryRanges` (if required - if memory type is not `HOST_COHERENT`).++It is also the safest one, because using `memcpy` avoids a risk of accidentally introducing memory reads+(e.g. by doing `pMappedVectors[i] += v`), which may be very slow on memory types that are not `HOST_CACHED`.++\code+struct ConstantBuffer+{+    ...+};+ConstantBuffer constantBufferData = ...++VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = sizeof(ConstantBuffer);+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;++VkBuffer buf;+VmaAllocation alloc;+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);++vmaCopyMemoryToAllocation(allocator, &constantBufferData, alloc, 0, sizeof(ConstantBuffer));+\endcode++Copy in the other direction - from an allocation to a host pointer can be performed the same way using function vmaCopyAllocationToMemory().++\section memory_mapping_mapping_functions Mapping functions++The library provides following functions for mapping of a specific allocation: vmaMapMemory(), vmaUnmapMemory().+They are safer and more convenient to use than standard Vulkan functions.+You can map an allocation multiple times simultaneously - mapping is reference-counted internally.+You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.+The way it is implemented is that the library always maps entire memory block, not just region of the allocation.+For further details, see description of vmaMapMemory() function.+Example:++\code+// Having these objects initialized:+struct ConstantBuffer+{+    ...+};+ConstantBuffer constantBufferData = ...++VmaAllocator allocator = ...+VkBuffer constantBuffer = ...+VmaAllocation constantBufferAllocation = ...++// You can map and fill your buffer using following code:++void* mappedData;+vmaMapMemory(allocator, constantBufferAllocation, &mappedData);+memcpy(mappedData, &constantBufferData, sizeof(constantBufferData));+vmaUnmapMemory(allocator, constantBufferAllocation);+\endcode++When mapping, you may see a warning from Vulkan validation layer similar to this one:++<i>Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.</i>++It happens because the library maps entire `VkDeviceMemory` block, where different+types of images and buffers may end up together, especially on GPUs with unified memory like Intel.+You can safely ignore it if you are sure you access only memory of the intended+object that you wanted to map.+++\section memory_mapping_persistently_mapped_memory Persistently mapped memory++Keeping your memory persistently mapped is generally OK in Vulkan.+You don't need to unmap it before using its data on the GPU.+The library provides a special feature designed for that:+Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in+VmaAllocationCreateInfo::flags stay mapped all the time,+so you can just access CPU pointer to it any time+without a need to call any "map" or "unmap" function.+Example:++\code+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = sizeof(ConstantBuffer);+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |+    VMA_ALLOCATION_CREATE_MAPPED_BIT;++VkBuffer buf;+VmaAllocation alloc;+VmaAllocationInfo allocInfo;+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);++// Buffer is already mapped. You can access its memory.+memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData));+\endcode++\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up+in a mappable memory type.+For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or+#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.+#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation.+For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading.++\section memory_mapping_cache_control Cache flush and invalidate++Memory in Vulkan doesn't need to be unmapped before using it on GPU,+but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set,+you need to manually **invalidate** cache before reading of mapped pointer+and **flush** cache after writing to mapped pointer.+Map/unmap operations don't do that automatically.+Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`,+`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient+functions that refer to given allocation object: vmaFlushAllocation(),+vmaInvalidateAllocation(),+or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations().++Regions of memory specified for flush/invalidate must be aligned to+`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library.+In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations+within blocks are aligned to this value, so their offsets are always multiply of+`nonCoherentAtomSize` and two different allocations never share same "line" of this size.++Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA)+currently provide `HOST_COHERENT` flag on all memory types that are+`HOST_VISIBLE`, so on PC you may not need to bother.+++\page staying_within_budget Staying within budget++When developing a graphics-intensive game or program, it is important to avoid allocating+more GPU memory than it is physically available. When the memory is over-committed,+various bad things can happen, depending on the specific GPU, graphics driver, and+operating system:++- It may just work without any problems.+- The application may slow down because some memory blocks are moved to system RAM+  and the GPU has to access them through PCI Express bus.+- A new allocation may take very long time to complete, even few seconds, and possibly+  freeze entire system.+- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.+- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST`+  returned somewhere later.++\section staying_within_budget_querying_for_budget Querying for budget++To query for current memory usage and available budget, use function vmaGetHeapBudgets().+Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap.++Please note that this function returns different information and works faster than+vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every+allocation, while vmaCalculateStatistics() is intended to be used rarely,+only to obtain statistical information, e.g. for debugging purposes.++It is recommended to use <b>VK_EXT_memory_budget</b> device extension to obtain information+about the budget from Vulkan device. VMA is able to use this extension automatically.+When not enabled, the allocator behaves same way, but then it estimates current usage+and available budget based on its internal information and Vulkan memory heap sizes,+which may be less precise. In order to use this extension:++1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2+   required by it are available and enable them. Please note that the first is a device+   extension and the second is instance extension!+2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object.+3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from+   Vulkan inside of it to avoid overhead of querying it with every allocation.++\section staying_within_budget_controlling_memory_usage Controlling memory usage++There are many ways in which you can try to stay within the budget.++First, when making new allocation requires allocating a new memory block, the library+tries not to exceed the budget automatically. If a block with default recommended size+(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even+dedicated memory for just this resource.++If the size of the requested resource plus current memory usage is more than the+budget, by default the library still tries to create it, leaving it to the Vulkan+implementation whether the allocation succeeds or fails. You can change this behavior+by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is+not made if it would exceed the budget or if the budget is already exceeded.+VMA then tries to make the allocation from the next eligible Vulkan memory type.+If all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.+Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag+when creating resources that are not essential for the application (e.g. the texture+of a specific object) and not to pass it when creating critically important resources+(e.g. render targets).++On AMD graphics cards there is a custom vendor extension available: <b>VK_AMD_memory_overallocation_behavior</b>+that allows to control the behavior of the Vulkan implementation in out-of-memory cases -+whether it should fail with an error code or still allow the allocation.+Usage of this extension involves only passing extra structure on Vulkan device creation,+so it is out of scope of this library.++Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure+a new allocation is created only when it fits inside one of the existing memory blocks.+If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`.+This also ensures that the function call is very fast because it never goes to Vulkan+to obtain a new block.++\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount+set to more than 0 will currently try to allocate memory blocks without checking whether they+fit within budget.+++\page resource_aliasing Resource aliasing (overlap)++New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory+management, give an opportunity to alias (overlap) multiple resources in the+same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL).+It can be useful to save video memory, but it must be used with caution.++For example, if you know the flow of your whole render frame in advance, you+are going to use some intermediate textures or buffers only during a small range of render passes,+and you know these ranges don't overlap in time, you can bind these resources to+the same place in memory, even if they have completely different parameters (width, height, format etc.).++![Resource aliasing (overlap)](../gfx/Aliasing.png)++Such scenario is possible using VMA, but you need to create your images manually.+Then you need to calculate parameters of an allocation to be made using formula:++- allocation size = max(size of each image)+- allocation alignment = max(alignment of each image)+- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image)++Following example shows two different images bound to the same place in memory,+allocated to fit largest of them.++\code+// A 512x512 texture to be sampled.+VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };+img1CreateInfo.imageType = VK_IMAGE_TYPE_2D;+img1CreateInfo.extent.width = 512;+img1CreateInfo.extent.height = 512;+img1CreateInfo.extent.depth = 1;+img1CreateInfo.mipLevels = 10;+img1CreateInfo.arrayLayers = 1;+img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB;+img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;+img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;+img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;+img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;++// A full screen texture to be used as color attachment.+VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };+img2CreateInfo.imageType = VK_IMAGE_TYPE_2D;+img2CreateInfo.extent.width = 1920;+img2CreateInfo.extent.height = 1080;+img2CreateInfo.extent.depth = 1;+img2CreateInfo.mipLevels = 1;+img2CreateInfo.arrayLayers = 1;+img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;+img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;+img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;+img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;+img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;++VkImage img1;+res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1);+VkImage img2;+res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2);++VkMemoryRequirements img1MemReq;+vkGetImageMemoryRequirements(device, img1, &img1MemReq);+VkMemoryRequirements img2MemReq;+vkGetImageMemoryRequirements(device, img2, &img2MemReq);++VkMemoryRequirements finalMemReq = {};+finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size);+finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment);+finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits;+// Validate if(finalMemReq.memoryTypeBits != 0)++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;++VmaAllocation alloc;+res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr);++res = vmaBindImageMemory(allocator, alloc, img1);+res = vmaBindImageMemory(allocator, alloc, img2);++// You can use img1, img2 here, but not at the same time!++vmaFreeMemory(allocator, alloc);+vkDestroyImage(allocator, img2, nullptr);+vkDestroyImage(allocator, img1, nullptr);+\endcode++VMA also provides convenience functions that create a buffer or image and bind it to memory+represented by an existing #VmaAllocation:+vmaCreateAliasingBuffer(), vmaCreateAliasingBuffer2(),+vmaCreateAliasingImage(), vmaCreateAliasingImage2().+Versions with "2" offer additional parameter `allocationLocalOffset`.++Remember that using resources that alias in memory requires proper synchronization.+You need to issue a memory barrier to make sure commands that use `img1` and `img2`+don't overlap on GPU timeline.+You also need to treat a resource after aliasing as uninitialized - containing garbage data.+For example, if you use `img1` and then want to use `img2`, you need to issue+an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`.++Additional considerations:++- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases.+See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag.+- You can create more complex layout where different images and buffers are bound+at different offsets inside one large allocation. For example, one can imagine+a big texture used in some render passes, aliasing with a set of many small buffers+used between in some further passes. To bind a resource at non-zero offset in an allocation,+use vmaBindBufferMemory2() / vmaBindImageMemory2().+- Before allocating memory for the resources you want to alias, check `memoryTypeBits`+returned in memory requirements of each resource to make sure the bits overlap.+Some GPUs may expose multiple memory types suitable e.g. only for buffers or+images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your+resources may be disjoint. Aliasing them is not possible in that case.+++\page custom_memory_pools Custom memory pools++A memory pool contains a number of `VkDeviceMemory` blocks.+The library automatically creates and manages default pool for each memory type available on the device.+Default memory pool automatically grows in size.+Size of allocated blocks is also variable and managed automatically.+You are using default pools whenever you leave VmaAllocationCreateInfo::pool = null.++You can create custom pool and allocate memory out of it.+It can be useful if you want to:++- Keep certain kind of allocations separate from others.+- Enforce particular, fixed size of Vulkan memory blocks.+- Limit maximum amount of Vulkan memory allocated for that pool.+- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool.+- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in+  #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain.+- Perform defragmentation on a specific subset of your allocations.++To use custom memory pools:++-# Fill VmaPoolCreateInfo structure.+-# Call vmaCreatePool() to obtain #VmaPool handle.+-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle.+   You don't need to specify any other parameters of this structure, like `usage`.++Example:++\code+// Find memoryTypeIndex for the pool.+VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+sampleBufCreateInfo.size = 0x10000; // Doesn't matter.+sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo sampleAllocCreateInfo = {};+sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;++uint32_t memTypeIndex;+VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator,+    &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex);+// Check res...++// Create a pool that can have at most 2 blocks, 128 MiB each.+VmaPoolCreateInfo poolCreateInfo = {};+poolCreateInfo.memoryTypeIndex = memTypeIndex;+poolCreateInfo.blockSize = 128ULL * 1024 * 1024;+poolCreateInfo.maxBlockCount = 2;++VmaPool pool;+res = vmaCreatePool(allocator, &poolCreateInfo, &pool);+// Check res...++// Allocate a buffer out of it.+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = 1024;+bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.pool = pool;++VkBuffer buf;+VmaAllocation alloc;+res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);+// Check res...+\endcode++You have to free all allocations made from this pool before destroying it.++\code+vmaDestroyBuffer(allocator, buf, alloc);+vmaDestroyPool(allocator, pool);+\endcode++New versions of this library support creating dedicated allocations in custom pools.+It is supported only when VmaPoolCreateInfo::blockSize = 0.+To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and+VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.+++\section custom_memory_pools_MemTypeIndex Choosing memory type index++When creating a pool, you must explicitly specify memory type index.+To find the one suitable for your buffers or images, you can use helper functions+vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo().+You need to provide structures with example parameters of buffers or images+that you are going to create in that pool.++\code+VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+exampleBufCreateInfo.size = 1024; // Doesn't matter+exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;++uint32_t memTypeIndex;+vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex);++VmaPoolCreateInfo poolCreateInfo = {};+poolCreateInfo.memoryTypeIndex = memTypeIndex;+// ...+\endcode++When creating buffers/images allocated in that pool, provide following parameters:++- `VkBufferCreateInfo`: Prefer to pass same parameters as above.+  Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior.+  Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers+  or the other way around.+- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member.+  Other members are ignored anyway.+++\section custom_memory_pools_when_not_use When not to use custom pools++Custom pools are commonly overused by VMA users.+While it may feel natural to keep some logical groups of resources separate in memory,+in most cases it does more harm than good.+Using custom pool shouldn't be your first choice.+Instead, please make all allocations from default pools first and only use custom pools+if you can prove and measure that it is beneficial in some way,+e.g. it results in lower memory usage, better performance, etc.++Using custom pools has disadvantages:++- Each pool has its own collection of `VkDeviceMemory` blocks.+  Some of them may be partially or even completely empty.+  Spreading allocations across multiple pools increases the amount of wasted (allocated but unbound) memory.+- You must manually choose specific memory type to be used by a custom pool (set as VmaPoolCreateInfo::memoryTypeIndex).+  When using default pools, best memory type for each of your allocations can be selected automatically+  using a carefully design algorithm that works across all kinds of GPUs.+- If an allocation from a custom pool at specific memory type fails, entire allocation operation returns failure.+  When using default pools, VMA tries another compatible memory type.+- If you set VmaPoolCreateInfo::blockSize != 0, each memory block has the same size,+  while default pools start from small blocks and only allocate next blocks larger and larger+  up to the preferred block size.++Many of the common concerns can be addressed in a different way than using custom pools:++- If you want to keep your allocations of certain size (small versus large) or certain lifetime (transient versus long lived)+  separate, you likely don't need to.+  VMA uses a high quality allocation algorithm that manages memory well in various cases.+  Please measure and check if using custom pools provides a benefit.+- If you want to keep your images and buffers separate, you don't need to.+  VMA respects `bufferImageGranularity` limit automatically.+- If you want to keep your mapped and not mapped allocations separate, you don't need to.+  VMA respects `nonCoherentAtomSize` limit automatically.+  It also maps only those `VkDeviceMemory` blocks that need to map any allocation.+  It even tries to keep mappable and non-mappable allocations in separate blocks to minimize the amount of mapped memory.+- If you want to choose a custom size for the default memory block, you can set it globally instead+  using VmaAllocatorCreateInfo::preferredLargeHeapBlockSize.+- If you want to select specific memory type for your allocation,+  you can set VmaAllocationCreateInfo::memoryTypeBits to `(1U << myMemoryTypeIndex)` instead.+- If you need to create a buffer with certain minimum alignment, you can still do it+  using default pools with dedicated function vmaCreateBufferWithAlignment().+++\section linear_algorithm Linear allocation algorithm++Each Vulkan memory block managed by this library has accompanying metadata that+keeps track of used and unused regions. By default, the metadata structure and+algorithm tries to find best place for new allocations among free regions to+optimize memory usage. This way you can allocate and free objects in any order.++![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png)++Sometimes there is a need to use simpler, linear allocation algorithm. You can+create custom pool that uses such algorithm by adding flag+#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating+#VmaPool object. Then an alternative metadata management is used. It always+creates new allocations after last one and doesn't reuse free regions after+allocations freed in the middle. It results in better allocation performance and+less memory consumed by metadata.++![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png)++With this one flag, you can create a custom pool that can be used in many ways:+free-at-once, stack, double stack, and ring buffer. See below for details.+You don't need to specify explicitly which of these options you are going to use - it is detected automatically.++\subsection linear_algorithm_free_at_once Free-at-once++In a pool that uses linear algorithm, you still need to free all the allocations+individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free+them in any order. New allocations are always made after last one - free space+in the middle is not reused. However, when you release all the allocation and+the pool becomes empty, allocation starts from the beginning again. This way you+can use linear algorithm to speed up creation of allocations that you are going+to release all at once.++![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)++This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount+value that allows multiple memory blocks.++\subsection linear_algorithm_stack Stack++When you free an allocation that was created last, its space can be reused.+Thanks to this, if you always release allocations in the order opposite to their+creation (LIFO - Last In First Out), you can achieve behavior of a stack.++![Stack](../gfx/Linear_allocator_4_stack.png)++This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount+value that allows multiple memory blocks.++\subsection linear_algorithm_double_stack Double stack++The space reserved by a custom pool with linear algorithm may be used by two+stacks:++- First, default one, growing up from offset 0.+- Second, "upper" one, growing down from the end towards lower offsets.++To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT+to VmaAllocationCreateInfo::flags.++![Double stack](../gfx/Linear_allocator_7_double_stack.png)++Double stack is available only in pools with one memory block -+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.++When the two stacks' ends meet so there is not enough space between them for a+new allocation, such allocation fails with usual+`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.++\subsection linear_algorithm_ring_buffer Ring buffer++When you free some allocations from the beginning and there is not enough free space+for a new one at the end of a pool, allocator's "cursor" wraps around to the+beginning and starts allocation there. Thanks to this, if you always release+allocations in the same order as you created them (FIFO - First In First Out),+you can achieve behavior of a ring buffer / queue.++![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png)++Ring buffer is available only in pools with one memory block -+VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.++\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.+++\page defragmentation Defragmentation++Interleaved allocations and deallocations of many objects of varying size can+cause fragmentation over time, which can lead to a situation where the library is unable+to find a continuous range of free memory for a new allocation despite there is+enough free space, just scattered across many small free ranges between existing+allocations.++To mitigate this problem, you can use defragmentation feature.+It doesn't happen automatically though and needs your cooperation,+because VMA is a low level library that only allocates memory.+It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures.+It cannot copy their contents as it doesn't record any commands to a command buffer.++Example:++\code+VmaDefragmentationInfo defragInfo = {};+defragInfo.pool = myPool;+defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT;++VmaDefragmentationContext defragCtx;+VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx);+// Check res...++for(;;)+{+    VmaDefragmentationPassMoveInfo pass;+    res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass);+    if(res == VK_SUCCESS)+        break;+    else if(res != VK_INCOMPLETE)+        // Handle error...++    for(uint32_t i = 0; i < pass.moveCount; ++i)+    {+        // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents.+        VmaAllocationInfo allocInfo;+        vmaGetAllocationInfo(allocator, pass.pMoves[i].srcAllocation, &allocInfo);+        MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData;++        // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset.+        VkImageCreateInfo imgCreateInfo = ...+        VkImage newImg;+        res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg);+        // Check res...+        res = vmaBindImageMemory(allocator, pass.pMoves[i].dstTmpAllocation, newImg);+        // Check res...++        // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place.+        vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...);+    }++    // Make sure the copy commands finished executing.+    vkWaitForFences(...);++    // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation.+    for(uint32_t i = 0; i < pass.moveCount; ++i)+    {+        // ...+        vkDestroyImage(device, resData->img, nullptr);+    }++    // Update appropriate descriptors to point to the new places...++    res = vmaEndDefragmentationPass(allocator, defragCtx, &pass);+    if(res == VK_SUCCESS)+        break;+    else if(res != VK_INCOMPLETE)+        // Handle error...+}++vmaEndDefragmentation(allocator, defragCtx, nullptr);+\endcode++Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage()+create/destroy an allocation and a buffer/image at once, these are just a shortcut for+creating the resource, allocating memory, and binding them together.+Defragmentation works on memory allocations only. You must handle the rest manually.+Defragmentation is an iterative process that should repreat "passes" as long as related functions+return `VK_INCOMPLETE` not `VK_SUCCESS`.+In each pass:++1. vmaBeginDefragmentationPass() function call:+   - Calculates and returns the list of allocations to be moved in this pass.+     Note this can be a time-consuming process.+   - Reserves destination memory for them by creating temporary destination allocations+     that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo().+2. Inside the pass, **you should**:+   - Inspect the returned list of allocations to be moved.+   - Create new buffers/images and bind them at the returned destination temporary allocations.+   - Copy data from source to destination resources if necessary.+   - Destroy the source buffers/images, but NOT their allocations.+3. vmaEndDefragmentationPass() function call:+   - Frees the source memory reserved for the allocations that are moved.+   - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory.+   - Frees `VkDeviceMemory` blocks that became empty.++Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter.+Defragmentation algorithm tries to move all suitable allocations.+You can, however, refuse to move some of them inside a defragmentation pass, by setting+`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.+This is not recommended and may result in suboptimal packing of the allocations after defragmentation.+If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool.++Inside a pass, for each allocation that should be moved:++- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`.+  - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass().+- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared,+  filled, and used temporarily in each rendering frame, you can just recreate this image+  without copying its data.+- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU+  using `memcpy()`.+- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE.+  This will cancel the move.+  - vmaEndDefragmentationPass() will then free the destination memory+    not the source memory of the allocation, leaving it unchanged.+- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time),+  you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY.+  - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object.++You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool+(like in the example above) or all the default pools by setting this member to null.++Defragmentation is always performed in each pool separately.+Allocations are never moved between different Vulkan memory types.+The size of the destination memory reserved for a moved allocation is the same as the original one.+Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation.+Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones.++You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved+in each pass, e.g. to call it in sync with render frames and not to experience too big hitches.+See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass.++It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA+usage, possibly from multiple threads, with the exception that allocations+returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended.++<b>Mapping</b> is preserved on allocations that are moved during defragmentation.+Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations+are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried+using VmaAllocationInfo::pMappedData.++\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT.+++\page statistics Statistics++This library contains several functions that return information about its internal state,+especially the amount of memory allocated from Vulkan.++\section statistics_numeric_statistics Numeric statistics++If you need to obtain basic statistics about memory usage per heap, together with current budget,+you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget.+This is useful to keep track of memory usage and stay within budget+(see also \ref staying_within_budget).+Example:++\code+uint32_t heapIndex = ...++VmaBudget budgets[VK_MAX_MEMORY_HEAPS];+vmaGetHeapBudgets(allocator, budgets);++printf("My heap currently has %u allocations taking %llu B,\n",+    budgets[heapIndex].statistics.allocationCount,+    budgets[heapIndex].statistics.allocationBytes);+printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n",+    budgets[heapIndex].statistics.blockCount,+    budgets[heapIndex].statistics.blockBytes);+printf("Vulkan reports total usage %llu B with budget %llu B.\n",+    budgets[heapIndex].usage,+    budgets[heapIndex].budget);+\endcode++You can query for more detailed statistics per memory heap, type, and totals,+including minimum and maximum allocation size and unused range size,+by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics.+This function is slower though, as it has to traverse all the internal data structures,+so it should be used only for debugging purposes.++You can query for statistics of a custom pool using function vmaGetPoolStatistics()+or vmaCalculatePoolStatistics().++You can query for information about a specific allocation using function vmaGetAllocationInfo().+It fill structure #VmaAllocationInfo.++\section statistics_json_dump JSON dump++You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString().+The result is guaranteed to be correct JSON.+It uses ANSI encoding.+Any strings provided by user (see [Allocation names](@ref allocation_names))+are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding,+this JSON string can be treated as using this encoding.+It must be freed using function vmaFreeStatsString().++The format of this JSON string is not part of official documentation of the library,+but it will not change in backward-incompatible way without increasing library major version number+and appropriate mention in changelog.++The JSON string contains all the data that can be obtained using vmaCalculateStatistics().+It can also contain detailed map of allocated memory blocks and their regions -+free and occupied by allocations.+This allows e.g. to visualize the memory or assess fragmentation.+++\page allocation_annotation Allocation names and user data++\section allocation_user_data Allocation user data++You can annotate allocations with your own information, e.g. for debugging purposes.+To do that, fill VmaAllocationCreateInfo::pUserData field when creating+an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer,+some handle, index, key, ordinal number or any other value that would associate+the allocation with your custom metadata.+It is useful to identify appropriate data structures in your engine given #VmaAllocation,+e.g. when doing \ref defragmentation.++\code+VkBufferCreateInfo bufCreateInfo = ...++MyBufferMetadata* pMetadata = CreateBufferMetadata();++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.pUserData = pMetadata;++VkBuffer buffer;+VmaAllocation allocation;+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr);+\endcode++The pointer may be later retrieved as VmaAllocationInfo::pUserData:++\code+VmaAllocationInfo allocInfo;+vmaGetAllocationInfo(allocator, allocation, &allocInfo);+MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData;+\endcode++It can also be changed using function vmaSetAllocationUserData().++Values of (non-zero) allocations' `pUserData` are printed in JSON report created by+vmaBuildStatsString() in hexadecimal form.++\section allocation_names Allocation names++An allocation can also carry a null-terminated string, giving a name to the allocation.+To set it, call vmaSetAllocationName().+The library creates internal copy of the string, so the pointer you pass doesn't need+to be valid for whole lifetime of the allocation. You can free it after the call.++\code+std::string imageName = "Texture: ";+imageName += fileName;+vmaSetAllocationName(allocator, allocation, imageName.c_str());+\endcode++The string can be later retrieved by inspecting VmaAllocationInfo::pName.+It is also printed in JSON report created by vmaBuildStatsString().++\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it.+You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library.+++\page virtual_allocator Virtual allocator++As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator".+It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block".+You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan.+A common use case is sub-allocation of pieces of one large GPU buffer.++\section virtual_allocator_creating_virtual_block Creating virtual block++To use this functionality, there is no main "allocator" object.+You don't need to have #VmaAllocator object created.+All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator:++-# Fill in #VmaVirtualBlockCreateInfo structure.+-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object.++Example:++\code+VmaVirtualBlockCreateInfo blockCreateInfo = {};+blockCreateInfo.size = 1048576; // 1 MB++VmaVirtualBlock block;+VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block);+\endcode++\section virtual_allocator_making_virtual_allocations Making virtual allocations++#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions+using the same code as the main Vulkan memory allocator.+Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type+that represents an opaque handle to an allocation within the virtual block.++In order to make such allocation:++-# Fill in #VmaVirtualAllocationCreateInfo structure.+-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation.+   You can also receive `VkDeviceSize offset` that was assigned to the allocation.++Example:++\code+VmaVirtualAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.size = 4096; // 4 KB++VmaVirtualAllocation alloc;+VkDeviceSize offset;+res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset);+if(res == VK_SUCCESS)+{+    // Use the 4 KB of your memory starting at offset.+}+else+{+    // Allocation failed - no space for it could be found. Handle this error!+}+\endcode++\section virtual_allocator_deallocation Deallocation++When no longer needed, an allocation can be freed by calling vmaVirtualFree().+You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate()+called for the same #VmaVirtualBlock.++When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock().+All allocations must be freed before the block is destroyed, which is checked internally by an assert.+However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once -+a feature not available in normal Vulkan memory allocator. Example:++\code+vmaVirtualFree(block, alloc);+vmaDestroyVirtualBlock(block);+\endcode++\section virtual_allocator_allocation_parameters Allocation parameters++You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData().+Its default value is null.+It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some+larger data structure containing more information. Example:++\code+struct CustomAllocData+{+    std::string m_AllocName;+};+CustomAllocData* allocData = new CustomAllocData();+allocData->m_AllocName = "My allocation 1";+vmaSetVirtualAllocationUserData(block, alloc, allocData);+\endcode++The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function+vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo.+If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation!+Example:++\code+VmaVirtualAllocationInfo allocInfo;+vmaGetVirtualAllocationInfo(block, alloc, &allocInfo);+delete (CustomAllocData*)allocInfo.pUserData;++vmaVirtualFree(block, alloc);+\endcode++\section virtual_allocator_alignment_and_units Alignment and units++It feels natural to express sizes and offsets in bytes.+If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member+VmaVirtualAllocationCreateInfo::alignment to request it. Example:++\code+VmaVirtualAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.size = 4096; // 4 KB+allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B++VmaVirtualAllocation alloc;+res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr);+\endcode++Alignments of different allocations made from one block may vary.+However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`,+you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes.+It might be more convenient, but you need to make sure to use this new unit consistently in all the places:++- VmaVirtualBlockCreateInfo::size+- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment+- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset++\section virtual_allocator_statistics Statistics++You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics()+(to get brief statistics that are fast to calculate)+or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate).+The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator.+Example:++\code+VmaStatistics stats;+vmaGetVirtualBlockStatistics(block, &stats);+printf("My virtual block has %llu bytes used by %u virtual allocations\n",+    stats.allocationBytes, stats.allocationCount);+\endcode++You can also request a full list of allocations and free regions as a string in JSON format by calling+vmaBuildVirtualBlockStatsString().+Returned string must be later freed using vmaFreeVirtualBlockStatsString().+The format of this string differs from the one returned by the main Vulkan allocator, but it is similar.++\section virtual_allocator_additional_considerations Additional considerations++The "virtual allocator" functionality is implemented on a level of individual memory blocks.+Keeping track of a whole collection of blocks, allocating new ones when out of free space,+deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user.++Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory.+See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT).+You can find their description in chapter \ref custom_memory_pools.+Allocation strategies are also supported.+See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT).++Following features are supported only by the allocator of the real GPU memory and not by virtual allocations:+buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`.+++\page debugging_memory_usage Debugging incorrect memory usage++If you suspect a bug with memory usage, like usage of uninitialized memory or+memory being overwritten out of bounds of an allocation,+you can use debug features of this library to verify this.++\section debugging_memory_usage_initialization Memory initialization++If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used,+you can enable automatic memory initialization to verify this.+To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1.++\code+#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1+#include "vk_mem_alloc.h"+\endcode++It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`.+Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`.+Memory is automatically mapped and unmapped if necessary.++If you find these values while debugging your program, good chances are that you incorrectly+read Vulkan memory that is allocated but not initialized, or already freed, respectively.++Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped.+It works also with dedicated allocations.++\section debugging_memory_usage_margins Margins++By default, allocations are laid out in memory blocks next to each other if possible+(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`).++![Allocations without margin](../gfx/Margins_1.png)++Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified+number of bytes as a margin after every allocation.++\code+#define VMA_DEBUG_MARGIN 16+#include "vk_mem_alloc.h"+\endcode++![Allocations with margin](../gfx/Margins_2.png)++If your bug goes away after enabling margins, it means it may be caused by memory+being overwritten outside of allocation boundaries. It is not 100% certain though.+Change in application behavior may also be caused by different order and distribution+of allocations across memory blocks after margins are applied.++Margins work with all types of memory.++Margin is applied only to allocations made out of memory blocks and not to dedicated+allocations, which have their own memory block of specific size.+It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag+or those automatically decided to put into dedicated allocations, e.g. due to its+large size or recommended by VK_KHR_dedicated_allocation extension.++Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space.++Note that enabling margins increases memory usage and fragmentation.++Margins do not apply to \ref virtual_allocator.++\section debugging_memory_usage_corruption_detection Corruption detection++You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation+of contents of the margins.++\code+#define VMA_DEBUG_MARGIN 16+#define VMA_DEBUG_DETECT_CORRUPTION 1+#include "vk_mem_alloc.h"+\endcode++When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN`+(it must be multiply of 4) after every allocation is filled with a magic number.+This idea is also know as "canary".+Memory is automatically mapped and unmapped if necessary.++This number is validated automatically when the allocation is destroyed.+If it is not equal to the expected value, `VMA_ASSERT()` is executed.+It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation,+which indicates a serious bug.++You can also explicitly request checking margins of all allocations in all memory blocks+that belong to specified memory types by using function vmaCheckCorruption(),+or in memory blocks that belong to specified custom pool, by using function+vmaCheckPoolCorruption().++Margin validation (corruption detection) works only for memory types that are+`HOST_VISIBLE` and `HOST_COHERENT`.+++\section debugging_memory_usage_leak_detection Leak detection features++At allocation and allocator destruction time VMA checks for unfreed and unmapped blocks using+`VMA_ASSERT_LEAK()`. This macro defaults to an assertion, triggering a typically fatal error in Debug+builds, and doing nothing in Release builds. You can provide your own definition of `VMA_ASSERT_LEAK()`+to change this behavior.++At memory block destruction time VMA lists out all unfreed allocations using the `VMA_LEAK_LOG_FORMAT()`+macro, which defaults to `VMA_DEBUG_LOG_FORMAT`, which in turn defaults to a no-op.+If you're having trouble with leaks - for example, the aforementioned assertion triggers, but you don't+quite know \em why -, overriding this macro to print out the the leaking blocks, combined with assigning+individual names to allocations using vmaSetAllocationName(), can greatly aid in fixing them.++\page other_api_interop Interop with other graphics APIs++VMA provides some features that help with interoperability with other graphics APIs, e.g. OpenGL.++\section opengl_interop_exporting_memory Exporting memory++If you want to attach `VkExportMemoryAllocateInfoKHR` or other structure to `pNext` chain of memory allocations made by the library:++You can create \ref custom_memory_pools for such allocations.+Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext+while creating the custom pool.+Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool,+not only while creating it, as no copy of the structure is made,+but its original pointer is used for each allocation instead.++If you want to export all memory allocated by VMA from certain memory types,+also dedicated allocations or other allocations made from default pools,+an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes.+It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library+through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type.+Please note that new versions of the library also support dedicated allocations created in custom pools.++You should not mix these two methods in a way that allows to apply both to the same memory type.+Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`.+++\section opengl_interop_custom_alignment Custom alignment++Buffers or images exported to a different API like OpenGL may require a different alignment,+higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`.+To impose such alignment:++You can create \ref custom_memory_pools for such allocations.+Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation+to be made out of this pool.+The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image+from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically.++If you want to create a buffer with a specific minimum alignment out of default pools,+use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`.++Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated+allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block.+You can ensure that an allocation is created as dedicated by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.+Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation.++\section opengl_interop_extended_allocation_information Extended allocation information++If you want to rely on VMA to allocate your buffers and images inside larger memory blocks,+but you need to know the size of the entire block and whether the allocation was made+with its own dedicated memory, use function vmaGetAllocationInfo2() to retrieve+extended allocation information in structure #VmaAllocationInfo2.++++\page usage_patterns Recommended usage patterns++Vulkan gives great flexibility in memory allocation.+This chapter shows the most common patterns.++See also slides from talk:+[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New)+++\section usage_patterns_gpu_only GPU-only resource++<b>When:</b>+Any resources that you frequently write and read on GPU,+e.g. images used as color attachments (aka "render targets"), depth-stencil attachments,+images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)").++<b>What to do:</b>+Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`.++\code+VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };+imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;+imgCreateInfo.extent.width = 3840;+imgCreateInfo.extent.height = 2160;+imgCreateInfo.extent.depth = 1;+imgCreateInfo.mipLevels = 1;+imgCreateInfo.arrayLayers = 1;+imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;+imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;+imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;+imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;+imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;+allocCreateInfo.priority = 1.0f;++VkImage img;+VmaAllocation alloc;+vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);+\endcode++<b>Also consider:</b>+Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT,+especially if they are large or if you plan to destroy and recreate them with different sizes+e.g. when display resolution changes.+Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later.+When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation+to decrease chances to be evicted to system memory by the operating system.++\section usage_patterns_staging_copy_upload Staging copy for upload++<b>When:</b>+A "staging" buffer than you want to map and fill from CPU code, then use as a source of transfer+to some GPU resource.++<b>What to do:</b>+Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT.+Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`.++\code+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = 65536;+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |+    VMA_ALLOCATION_CREATE_MAPPED_BIT;++VkBuffer buf;+VmaAllocation alloc;+VmaAllocationInfo allocInfo;+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);++...++memcpy(allocInfo.pMappedData, myData, myDataSize);+\endcode++<b>Also consider:</b>+You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped+using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above.+++\section usage_patterns_readback Readback++<b>When:</b>+Buffers for data written by or transferred from the GPU that you want to read back on the CPU,+e.g. results of some computations.++<b>What to do:</b>+Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.+Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`+and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`.++\code+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = 65536;+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT |+    VMA_ALLOCATION_CREATE_MAPPED_BIT;++VkBuffer buf;+VmaAllocation alloc;+VmaAllocationInfo allocInfo;+vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);++...++const float* downloadedData = (const float*)allocInfo.pMappedData;+\endcode+++\section usage_patterns_advanced_data_uploading Advanced data uploading++For resources that you frequently write on CPU via mapped pointer and+frequently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible:++-# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory,+   even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card,+   and make the device reach out to that resource directly.+   - Reads performed by the device will then go through PCI Express bus.+     The performance of this access may be limited, but it may be fine depending on the size+     of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity+     of access.+-# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips),+   a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL`+   (fast to access from the GPU). Then, it is likely the best choice for such type of resource.+-# Systems with a discrete graphics card and separate video memory may or may not expose+   a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR).+   If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS)+   that is available to CPU for mapping.+   - Writes performed by the host to that memory go through PCI Express bus.+     The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0,+     as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads.+-# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory,+   a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them.++Thankfully, VMA offers an aid to create and use such resources in the the way optimal+for the current Vulkan device. To help the library make the best choice,+use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with+#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT.+It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR),+but if no such memory type is available or allocation from it fails+(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS),+it will fall back to `DEVICE_LOCAL` memory for fast GPU access.+It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`,+so you need to create another "staging" allocation and perform explicit transfers.++\code+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = 65536;+bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |+    VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT |+    VMA_ALLOCATION_CREATE_MAPPED_BIT;++VkBuffer buf;+VmaAllocation alloc;+VmaAllocationInfo allocInfo;+VkResult result = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo);+// Check result...++VkMemoryPropertyFlags memPropFlags;+vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags);++if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)+{+    // Allocation ended up in a mappable memory and is already mapped - write to it directly.++    // [Executed in runtime]:+    memcpy(allocInfo.pMappedData, myData, myDataSize);+    result = vmaFlushAllocation(allocator, alloc, 0, VK_WHOLE_SIZE);+    // Check result...++    VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };+    bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;+    bufMemBarrier.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT;+    bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier.buffer = buf;+    bufMemBarrier.offset = 0;+    bufMemBarrier.size = VK_WHOLE_SIZE;++    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,+        0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);+}+else+{+    // Allocation ended up in a non-mappable memory - a transfer using a staging buffer is required.+    VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+    stagingBufCreateInfo.size = 65536;+    stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;++    VmaAllocationCreateInfo stagingAllocCreateInfo = {};+    stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+    stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT |+        VMA_ALLOCATION_CREATE_MAPPED_BIT;++    VkBuffer stagingBuf;+    VmaAllocation stagingAlloc;+    VmaAllocationInfo stagingAllocInfo;+    result = vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo,+        &stagingBuf, &stagingAlloc, &stagingAllocInfo);+    // Check result...++    // [Executed in runtime]:+    memcpy(stagingAllocInfo.pMappedData, myData, myDataSize);+    result = vmaFlushAllocation(allocator, stagingAlloc, 0, VK_WHOLE_SIZE);+    // Check result...++    VkBufferMemoryBarrier bufMemBarrier = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };+    bufMemBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;+    bufMemBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;+    bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier.buffer = stagingBuf;+    bufMemBarrier.offset = 0;+    bufMemBarrier.size = VK_WHOLE_SIZE;++    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,+        0, 0, nullptr, 1, &bufMemBarrier, 0, nullptr);++    VkBufferCopy bufCopy = {+        0, // srcOffset+        0, // dstOffset,+        myDataSize, // size+    };++    vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy);++    VkBufferMemoryBarrier bufMemBarrier2 = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER };+    bufMemBarrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;+    bufMemBarrier2.dstAccessMask = VK_ACCESS_UNIFORM_READ_BIT; // We created a uniform buffer+    bufMemBarrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;+    bufMemBarrier2.buffer = buf;+    bufMemBarrier2.offset = 0;+    bufMemBarrier2.size = VK_WHOLE_SIZE;++    vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,+        0, 0, nullptr, 1, &bufMemBarrier2, 0, nullptr);+}+\endcode++\section usage_patterns_other_use_cases Other use cases++Here are some other, less obvious use cases and their recommended settings:++- An image that is used only as transfer source and destination, but it should stay on the device,+  as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame,+  for temporal antialiasing or other temporal effects.+  - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`+  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO+- An image that is used only as transfer source and destination, but it should be placed+  in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict+  least recently used textures from VRAM.+  - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT`+  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST,+    as VMA needs a hint here to differentiate from the previous case.+- A buffer that you want to map and write from the CPU, directly read from the GPU+  (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or+  host memory due to its large size.+  - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT`+  - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST+  - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT+++\page configuration Configuration++Please check "CONFIGURATION SECTION" in the code to find macros that you can define+before each include of this file or change directly in this file to provide+your own implementation of basic facilities like assert, `min()` and `max()` functions,+mutex, atomic etc.++For example, define `VMA_ASSERT(expr)` before including the library to provide+custom implementation of the assertion, compatible with your project.+By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration+and empty otherwise.++Similarly, you can define `VMA_LEAK_LOG_FORMAT` macro to enable printing of leaked (unfreed) allocations,+including their names and other parameters. Example:++\code+#define VMA_LEAK_LOG_FORMAT(format, ...) do { \+        printf((format), __VA_ARGS__); \+        printf("\n"); \+    } while(false)+\endcode++\section config_Vulkan_functions Pointers to Vulkan functions++There are multiple ways to import pointers to Vulkan functions in the library.+In the simplest case you don't need to do anything.+If the compilation or linking of your program or the initialization of the #VmaAllocator+doesn't work for you, you can try to reconfigure it.++First, the allocator tries to fetch pointers to Vulkan functions linked statically,+like this:++\code+m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory;+\endcode++If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`.++Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions.+You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or+by using a helper library like [volk](https://github.com/zeux/volk).++Third, VMA tries to fetch remaining pointers that are still null by calling+`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own.+You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr.+Other pointers will be fetched automatically.+If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`.++Finally, all the function pointers required by the library (considering selected+Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null.+++\section custom_memory_allocator Custom host memory allocator++If you use custom allocator for CPU memory rather than default operator `new`+and `delete` from C++, you can make this library using your allocator as well+by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These+functions will be passed to Vulkan, as well as used by the library itself to+make any CPU-side allocations.++\section allocation_callbacks Device memory allocation callbacks++The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally.+You can setup callbacks to be informed about these calls, e.g. for the purpose+of gathering some statistics. To do it, fill optional member+VmaAllocatorCreateInfo::pDeviceMemoryCallbacks.++\section heap_memory_limit Device heap memory limit++When device memory of certain heap runs out of free space, new allocations may+fail (returning error code) or they may succeed, silently pushing some existing_+memory blocks from GPU VRAM to system RAM (which degrades performance). This+behavior is implementation-dependent - it depends on GPU vendor and graphics+driver.++On AMD cards it can be controlled while creating Vulkan device object by using+VK_AMD_memory_overallocation_behavior extension, if available.++Alternatively, if you want to test how your program behaves with limited amount of Vulkan device+memory available without switching your graphics card to one that really has+smaller VRAM, you can use a feature of this library intended for this purpose.+To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit.++++\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation++VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve+performance on some GPUs. It augments Vulkan API with possibility to query+driver whether it prefers particular buffer or image to have its own, dedicated+allocation (separate `VkDeviceMemory` block) for better efficiency - to be able+to do some internal optimizations. The extension is supported by this library.+It will be used automatically when enabled.++It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version+and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion,+you are all set.++Otherwise, if you want to use it as an extension:++1 . When creating Vulkan device, check if following 2 device extensions are+supported (call `vkEnumerateDeviceExtensionProperties()`).+If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`).++- VK_KHR_get_memory_requirements2+- VK_KHR_dedicated_allocation++If you enabled these extensions:++2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating+your #VmaAllocator to inform the library that you enabled required extensions+and you want the library to use them.++\code+allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;++vmaCreateAllocator(&allocatorInfo, &allocator);+\endcode++That is all. The extension will be automatically used whenever you create a+buffer using vmaCreateBuffer() or image using vmaCreateImage().++When using the extension together with Vulkan Validation Layer, you will receive+warnings like this:++_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._++It is OK, you should just ignore it. It happens because you use function+`vkGetBufferMemoryRequirements2KHR()` instead of standard+`vkGetBufferMemoryRequirements()`, while the validation layer seems to be+unaware of it.++To learn more about this extension, see:++- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation)+- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5)++++\page vk_ext_memory_priority VK_EXT_memory_priority++VK_EXT_memory_priority is a device extension that allows to pass additional "priority"+value to Vulkan memory allocations that the implementation may use prefer certain+buffers and images that are critical for performance to stay in device-local memory+in cases when the memory is over-subscribed, while some others may be moved to the system memory.++VMA offers convenient usage of this extension.+If you enable it, you can pass "priority" parameter when creating allocations or custom pools+and the library automatically passes the value to Vulkan using this extension.++If you want to use this extension in connection with VMA, follow these steps:++\section vk_ext_memory_priority_initialization Initialization++1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.+Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority".++2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.+Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned.+Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true.++3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority"+to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.++4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.+Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.+Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to+`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`.++5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you+have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT+to VmaAllocatorCreateInfo::flags.++\section vk_ext_memory_priority_usage Usage++When using this extension, you should initialize following member:++- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT.+- VmaPoolCreateInfo::priority when creating a custom pool.++It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5F`.+Memory allocated with higher value can be treated by the Vulkan implementation as higher priority+and so it can have lower chances of being pushed out to system memory, experiencing degraded performance.++It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images+as dedicated and set high priority to them. For example:++\code+VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };+imgCreateInfo.imageType = VK_IMAGE_TYPE_2D;+imgCreateInfo.extent.width = 3840;+imgCreateInfo.extent.height = 2160;+imgCreateInfo.extent.depth = 1;+imgCreateInfo.mipLevels = 1;+imgCreateInfo.arrayLayers = 1;+imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM;+imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;+imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;+imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;+imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;+allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;+allocCreateInfo.priority = 1.0f;++VkImage img;+VmaAllocation alloc;+vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr);+\endcode++`priority` member is ignored in the following situations:++- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters+  from the parameters passed in #VmaPoolCreateInfo when the pool was created.+- Allocations created in default pools: They inherit the priority from the parameters+  VMA used when creating default pools, which means `priority == 0.5F`.+++\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory++VK_AMD_device_coherent_memory is a device extension that enables access to+additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and+`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for+allocation of buffers intended for writing "breadcrumb markers" in between passes+or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases.++When the extension is available but has not been enabled, Vulkan physical device+still exposes those memory types, but their usage is forbidden. VMA automatically+takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt+to allocate memory of such type is made.++If you want to use this extension in connection with VMA, follow these steps:++\section vk_amd_device_coherent_memory_initialization Initialization++1) Call `vkEnumerateDeviceExtensionProperties` for the physical device.+Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory".++2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`.+Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned.+Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true.++3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory"+to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`.++4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`.+Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`.+Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to+`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`.++5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you+have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT+to VmaAllocatorCreateInfo::flags.++\section vk_amd_device_coherent_memory_usage Usage++After following steps described above, you can create VMA allocations and custom pools+out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible+devices. There are multiple ways to do it, for example:++- You can request or prefer to allocate out of such memory types by adding+  `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags+  or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with+  other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage.+- If you manually found memory type index to use for this purpose, force allocation+  from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1U << index`.++\section vk_amd_device_coherent_memory_more_information More information++To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html)++Example use of this extension can be found in the code of the sample and test suite+accompanying this library.+++\page vk_khr_external_memory_win32 VK_KHR_external_memory_win32++On Windows, the VK_KHR_external_memory_win32 device extension allows exporting a Win32 `HANDLE`+of a `VkDeviceMemory` block, to be able to reference the memory on other Vulkan logical devices or instances,+in multiple processes, and/or in multiple APIs.+VMA offers support for it.++\section vk_khr_external_memory_win32_initialization Initialization++1) Make sure the extension is defined in the code by including following header before including VMA:++\code+#include <vulkan/vulkan_win32.h>+\endcode++2) Check if "VK_KHR_external_memory_win32" is available among device extensions.+Enable it when creating the `VkDevice` object.++3) Enable the usage of this extension in VMA by setting flag #VMA_ALLOCATOR_CREATE_KHR_EXTERNAL_MEMORY_WIN32_BIT+when calling vmaCreateAllocator().++4) Make sure that VMA has access to the `vkGetMemoryWin32HandleKHR` function by either enabling `VMA_DYNAMIC_VULKAN_FUNCTIONS` macro+or setting VmaVulkanFunctions::vkGetMemoryWin32HandleKHR explicitly.+For more information, see \ref quick_start_initialization_importing_vulkan_functions.++\section vk_khr_external_memory_win32_preparations Preparations++You can find example usage among tests, in file "Tests.cpp", function `TestWin32Handles()`.++To use the extenion, buffers need to be created with `VkExternalMemoryBufferCreateInfoKHR` attached to their `pNext` chain,+and memory allocations need to be made with `VkExportMemoryAllocateInfoKHR` attached to their `pNext` chain.+To make use of them, you need to use \ref custom_memory_pools. Example:++\code+// Define an example buffer and allocation parameters.+VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,+    nullptr,+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT+};+VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+exampleBufCreateInfo.size = 0x10000; // Doesn't matter here.+exampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;+exampleBufCreateInfo.pNext = &externalMemBufCreateInfo;++VmaAllocationCreateInfo exampleAllocCreateInfo = {};+exampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;++// Find memory type index to use for the custom pool.+uint32_t memTypeIndex;+VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_Allocator,+    &exampleBufCreateInfo, &exampleAllocCreateInfo, &memTypeIndex);+// Check res...++// Create a custom pool.+constexpr static VkExportMemoryAllocateInfoKHR exportMemAllocInfo = {+    VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR,+    nullptr,+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT+};+VmaPoolCreateInfo poolCreateInfo = {};+poolCreateInfo.memoryTypeIndex = memTypeIndex;+poolCreateInfo.pMemoryAllocateNext = (void*)&exportMemAllocInfo;++VmaPool pool;+res = vmaCreatePool(g_Allocator, &poolCreateInfo, &pool);+// Check res...++// YOUR OTHER CODE COMES HERE....++// At the end, don't forget to destroy it!+vmaDestroyPool(g_Allocator, pool);+\endcode++Note that the structure passed as VmaPoolCreateInfo::pMemoryAllocateNext must remain alive and unchanged+for the whole lifetime of the custom pool, because it will be used when the pool allocates a new device memory block.+No copy is made internally. This is why variable `exportMemAllocInfo` is defined as `static`.++\section vk_khr_external_memory_win32_memory_allocation Memory allocation++Finally, you can create a buffer with an allocation out of the custom pool.+The buffer should use same flags as the sample buffer used to find the memory type.+It should also specify `VkExternalMemoryBufferCreateInfoKHR` in its `pNext` chain.++\code+VkExternalMemoryBufferCreateInfoKHR externalMemBufCreateInfo = {+    VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,+    nullptr,+    VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT+};+VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };+bufCreateInfo.size = // Your desired buffer size.+bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;+bufCreateInfo.pNext = &externalMemBufCreateInfo;++VmaAllocationCreateInfo allocCreateInfo = {};+allocCreateInfo.pool = pool;  // It is enough to set this one member.++VkBuffer buf;+VmaAllocation alloc;+res = vmaCreateBuffer(g_Allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);+// Check res...++// YOUR OTHER CODE COMES HERE....++// At the end, don't forget to destroy it!+vmaDestroyBuffer(g_Allocator, buf, alloc);+\endcode++If you need each allocation to have its own device memory block and start at offset 0, you can still do +by using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag. It works also with custom pools.++\section vk_khr_external_memory_win32_exporting_win32_handle Exporting Win32 handle++After the allocation is created, you can acquire a Win32 `HANDLE` to the `VkDeviceMemory` block it belongs to.+VMA function vmaGetMemoryWin32Handle() is a replacement of the Vulkan function `vkGetMemoryWin32HandleKHR`.++\code+HANDLE handle;+res = vmaGetMemoryWin32Handle(g_Allocator, alloc, nullptr, &handle);+// Check res...++// YOUR OTHER CODE COMES HERE....++// At the end, you must close the handle.+CloseHandle(handle);+\endcode++Documentation of the VK_KHR_external_memory_win32 extension states that:++> If handleType is defined as an NT handle, vkGetMemoryWin32HandleKHR must be called no more than once for each valid unique combination of memory and handleType.++This is ensured automatically inside VMA.+The library fetches the handle on first use, remembers it internally, and closes it when the memory block or dedicated allocation is destroyed.+Every time you call vmaGetMemoryWin32Handle(), VMA calls `DuplicateHandle` and returns a new handle that you need to close.++For further information, please check documentation of the vmaGetMemoryWin32Handle() function.   \page enabling_buffer_device_address Enabling buffer device address
changelog.md view
@@ -1,6 +1,9 @@ # Change Log -## WIP+## [0.11.1.0] - 2026-06-13+- Bump VMA to 3.3.0+  - Upstream updated, with no API changes.+- Raise upper bound on `vulkan`  ## [0.11.0.1] - 2023-10-17 - Raise upper bound on `vulkan`
package.yaml view
@@ -1,5 +1,5 @@ name: VulkanMemoryAllocator-version: "0.11.0.1"+version: "0.11.1.0" synopsis: Bindings to the VulkanMemoryAllocator library category: Graphics maintainer: Ellie Hermaszewska <live.long.and.prosper@monoid.al>@@ -11,7 +11,9 @@   - VulkanMemoryAllocator/include/vk_mem_alloc.h  library:-  source-dirs: src+  source-dirs:+    - src+    - src-manual   include-dirs:     VulkanMemoryAllocator/include @@ -20,7 +22,7 @@     src/lib.cpp   dependencies:     - base <5-    - vulkan >= 3.6 && < 3.27+    - vulkan >= 3.6 && < 3.28     - bytestring     - transformers     - vector
+ src-manual/VulkanMemoryAllocator/Utils.hs view
@@ -0,0 +1,64 @@+{-# LANGUAGE NamedFieldPuns #-}++{-| Hand-written scaffolding over the generated bindings.++'allocatorCreateInfo' fills in the one fiddly part of bringing up an+allocator with the dynamically loaded @vulkan@ bindings: VMA must be+pointed, via 'VulkanFunctions', at the same function pointers the+'Vk.Instance' and 'Vk.Device' were loaded with, and those live inside+the handles' command records. Pass the result to 'createAllocator' or+'withAllocator':++> (_, allocator) <-+>   withAllocator+>     (allocatorCreateInfo zero API_VERSION_1_3 inst phys dev)+>     allocate++For anything beyond the common case (heap limits, allocation callbacks,+…) update the returned create info before use.+-}+module VulkanMemoryAllocator.Utils+  ( allocatorCreateInfo+  ) where++import Data.Word (Word32)+import Foreign.Ptr (castFunPtr)+import qualified Vulkan.Core10 as Vk+import Vulkan.Dynamic (DeviceCmds (DeviceCmds, pVkGetDeviceProcAddr), InstanceCmds (InstanceCmds, pVkGetInstanceProcAddr))+import Vulkan.Zero (zero)+import VulkanMemoryAllocator (AllocatorCreateFlags, AllocatorCreateInfo, VulkanFunctions)+import qualified VulkanMemoryAllocator as AllocatorCreateInfo (AllocatorCreateInfo (..))+import qualified VulkanMemoryAllocator as VulkanFunctions (VulkanFunctions (..))++{- | A create info for an allocator serving the given device, with+'VulkanFunctions' wired to the function pointers the handles were loaded+with.+-}+allocatorCreateInfo+  :: AllocatorCreateFlags+  -> Word32+  {- ^ The Vulkan API version the application targets — the @apiVersion@+  of its 'Vk.ApplicationInfo', e.g. @API_VERSION_1_3@.+  -}+  -> Vk.Instance+  -> Vk.PhysicalDevice+  -> Vk.Device+  -> AllocatorCreateInfo+allocatorCreateInfo flags apiVersion inst phys dev =+  zero+    { AllocatorCreateInfo.flags = flags+    , AllocatorCreateInfo.physicalDevice = Vk.physicalDeviceHandle phys+    , AllocatorCreateInfo.device = dh+    , AllocatorCreateInfo.instance' = ih+    , AllocatorCreateInfo.vulkanApiVersion = apiVersion+    , AllocatorCreateInfo.vulkanFunctions = Just funs+    }+  where+    funs :: VulkanFunctions+    funs =+      zero+        { VulkanFunctions.vkGetInstanceProcAddr = castFunPtr pVkGetInstanceProcAddr+        , VulkanFunctions.vkGetDeviceProcAddr = castFunPtr pVkGetDeviceProcAddr+        }+    Vk.Instance ih InstanceCmds{pVkGetInstanceProcAddr} = inst+    Vk.Device dh DeviceCmds{pVkGetDeviceProcAddr} = dev