From 819860e1f032311c04c19a8252158c56d435c92f Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 4 Jul 2017 14:30:38 +0200 Subject: [PATCH 01/11] Submitting version 2.0.0-alpha.1 to separate branch. --- src/VulkanSample.cpp | 83 +- src/vk_mem_alloc.h | 2191 +++++++++++++++++++++++++++++++++--------- 2 files changed, 1789 insertions(+), 485 deletions(-) diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp index 375bcb1..4f2fed7 100644 --- a/src/VulkanSample.cpp +++ b/src/VulkanSample.cpp @@ -90,6 +90,7 @@ static VkDescriptorSet g_hDescriptorSet; // Automatically destroyed with m_Descr static VkSampler g_hSampler; static VkFormat g_DepthFormat; static VkImage g_hDepthImage; +static VmaAllocation g_hDepthImageAlloc; static VkImageView g_hDepthImageView; static VkSurfaceCapabilitiesKHR g_SurfaceCapabilities; @@ -111,11 +112,14 @@ static VkRenderPass g_hRenderPass; static VkPipeline g_hPipeline; static VkBuffer g_hVertexBuffer; +static VmaAllocation g_hVertexBufferAlloc; static VkBuffer g_hIndexBuffer; +static VmaAllocation g_hIndexBufferAlloc; static uint32_t g_VertexCount; static uint32_t g_IndexCount; static VkImage g_hTextureImage; +static VmaAllocation g_hTextureImageAlloc; static VkImageView g_hTextureImageView; static void BeginSingleTimeCommands() @@ -287,20 +291,24 @@ static void CreateMesh() vbInfo.size = vertexBufferSize; vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; vbInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VmaMemoryRequirements vbMemReq = {}; - vbMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; - VkMappedMemoryRange stagingVertexBufferMem; - VkBuffer stagingVertexBuffer = VK_NULL_HANDLE; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &stagingVertexBuffer, &stagingVertexBufferMem, nullptr) ); - void* pVbData = nullptr; - ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingVertexBufferMem, &pVbData) ); - memcpy(pVbData, vertices, vertexBufferSize); - vmaUnmapMemory(g_hAllocator, &stagingVertexBufferMem); + VmaMemoryRequirements vbMemReq = {}; + vbMemReq.usage = VMA_MEMORY_USAGE_CPU_ONLY; + vbMemReq.flags = VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT; + + VkBuffer stagingVertexBuffer = VK_NULL_HANDLE; + VmaAllocation stagingVertexBufferAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingVertexBufferAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &stagingVertexBuffer, &stagingVertexBufferAlloc, &stagingVertexBufferAllocInfo) ); + + memcpy(stagingVertexBufferAllocInfo.pMappedData, vertices, vertexBufferSize); + + // No need to flush stagingVertexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. vbInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; vbMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &g_hVertexBuffer, nullptr, nullptr) ); + vbMemReq.flags = 0; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &vbInfo, &vbMemReq, &g_hVertexBuffer, &g_hVertexBufferAlloc, nullptr) ); // Create index buffer @@ -308,20 +316,24 @@ static void CreateMesh() ibInfo.size = indexBufferSize; ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; ibInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VmaMemoryRequirements ibMemReq = {}; - ibMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; - VkMappedMemoryRange stagingIndexBufferMem; + ibMemReq.usage = VMA_MEMORY_USAGE_CPU_ONLY; + ibMemReq.flags = VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT; + VkBuffer stagingIndexBuffer = VK_NULL_HANDLE; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &stagingIndexBuffer, &stagingIndexBufferMem, nullptr) ); + VmaAllocation stagingIndexBufferAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingIndexBufferAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &stagingIndexBuffer, &stagingIndexBufferAlloc, &stagingIndexBufferAllocInfo) ); - void* pIbData = nullptr; - ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingIndexBufferMem, &pIbData) ); - memcpy(pIbData, indices, indexBufferSize); - vmaUnmapMemory(g_hAllocator, &stagingIndexBufferMem); + memcpy(stagingIndexBufferAllocInfo.pMappedData, indices, indexBufferSize); + + // No need to flush stagingIndexBuffer memory because CPU_ONLY memory is always HOST_COHERENT. ibInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT; ibMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY; - ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &g_hIndexBuffer, nullptr, nullptr) ); + ibMemReq.flags = 0; + ERR_GUARD_VULKAN( vmaCreateBuffer(g_hAllocator, &ibInfo, &ibMemReq, &g_hIndexBuffer, &g_hIndexBufferAlloc, nullptr) ); // Copy buffers @@ -341,8 +353,8 @@ static void CreateMesh() EndSingleTimeCommands(); - vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer); - vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer); + vmaDestroyBuffer(g_hAllocator, stagingIndexBuffer, stagingIndexBufferAlloc); + vmaDestroyBuffer(g_hAllocator, stagingVertexBuffer, stagingVertexBufferAlloc); } static void CopyImage(VkImage srcImage, VkImage dstImage, uint32_t width, uint32_t height, uint32_t mipLevel) @@ -468,14 +480,15 @@ static void CreateTexture(uint32_t sizeX, uint32_t sizeY) stagingImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; stagingImageInfo.samples = VK_SAMPLE_COUNT_1_BIT; stagingImageInfo.flags = 0; + VmaMemoryRequirements stagingImageMemReq = {}; - stagingImageMemReq.usage = VMA_MEMORY_USAGE_CPU_TO_GPU; + stagingImageMemReq.usage = VMA_MEMORY_USAGE_CPU_ONLY; + stagingImageMemReq.flags = VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT; + VkImage stagingImage = VK_NULL_HANDLE; - VkMappedMemoryRange stagingImageMem; - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageMemReq, &stagingImage, &stagingImageMem, nullptr) ); - - char* pImageData = nullptr; - ERR_GUARD_VULKAN( vmaMapMemory(g_hAllocator, &stagingImageMem, (void**)&pImageData) ); + VmaAllocation stagingImageAlloc = VK_NULL_HANDLE; + VmaAllocationInfo stagingImageAllocInfo = {}; + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &stagingImageInfo, &stagingImageMemReq, &stagingImage, &stagingImageAlloc, &stagingImageAllocInfo) ); VkImageSubresource imageSubresource = {}; imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; @@ -485,7 +498,7 @@ static void CreateTexture(uint32_t sizeX, uint32_t sizeY) VkSubresourceLayout imageLayout = {}; vkGetImageSubresourceLayout(g_hDevice, stagingImage, &imageSubresource, &imageLayout); - char* const pMipLevelData = pImageData + imageLayout.offset; + char* const pMipLevelData = (char*)stagingImageAllocInfo.pMappedData + imageLayout.offset; uint8_t* pRowData = (uint8_t*)pMipLevelData; for(uint32_t y = 0; y < sizeY; ++y) { @@ -502,7 +515,7 @@ static void CreateTexture(uint32_t sizeX, uint32_t sizeY) pRowData += imageLayout.rowPitch; } - vmaUnmapMemory(g_hAllocator, &stagingImageMem); + // No need to flush stagingImage memory because CPU_ONLY memory is always HOST_COHERENT. VkImageCreateInfo imageInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; imageInfo.imageType = VK_IMAGE_TYPE_2D; @@ -520,7 +533,7 @@ static void CreateTexture(uint32_t sizeX, uint32_t sizeY) imageInfo.flags = 0; VmaMemoryRequirements imageMemReq = {}; imageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY; - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageMemReq, &g_hTextureImage, nullptr, nullptr) ); + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &imageInfo, &imageMemReq, &g_hTextureImage, &g_hTextureImageAlloc, nullptr) ); TransitionImageLayout( stagingImage, @@ -542,7 +555,7 @@ static void CreateTexture(uint32_t sizeX, uint32_t sizeY) VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - vmaDestroyImage(g_hAllocator, stagingImage); + vmaDestroyImage(g_hAllocator, stagingImage, stagingImageAlloc); // Create ImageView @@ -756,7 +769,7 @@ static void CreateSwapchain() VmaMemoryRequirements depthImageMemReq = {}; depthImageMemReq.usage = VMA_MEMORY_USAGE_GPU_ONLY; - ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageMemReq, &g_hDepthImage, nullptr, nullptr) ); + ERR_GUARD_VULKAN( vmaCreateImage(g_hAllocator, &depthImageInfo, &depthImageMemReq, &g_hDepthImage, &g_hDepthImageAlloc, nullptr) ); VkImageViewCreateInfo depthImageViewInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; depthImageViewInfo.image = g_hDepthImage; @@ -1084,7 +1097,7 @@ static void DestroySwapchain(bool destroyActualSwapchain) } if(g_hDepthImage != VK_NULL_HANDLE) { - vmaDestroyImage(g_hAllocator, g_hDepthImage); + vmaDestroyImage(g_hAllocator, g_hDepthImage, g_hDepthImageAlloc); g_hDepthImage = VK_NULL_HANDLE; } @@ -1391,18 +1404,18 @@ static void FinalizeApplication() } if(g_hTextureImage != VK_NULL_HANDLE) { - vmaDestroyImage(g_hAllocator, g_hTextureImage); + vmaDestroyImage(g_hAllocator, g_hTextureImage, g_hTextureImageAlloc); g_hTextureImage = VK_NULL_HANDLE; } if(g_hIndexBuffer != VK_NULL_HANDLE) { - vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer); + vmaDestroyBuffer(g_hAllocator, g_hIndexBuffer, g_hIndexBufferAlloc); g_hIndexBuffer = VK_NULL_HANDLE; } if(g_hVertexBuffer != VK_NULL_HANDLE) { - vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer); + vmaDestroyBuffer(g_hAllocator, g_hVertexBuffer, g_hVertexBufferAlloc); g_hVertexBuffer = VK_NULL_HANDLE; } diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 64503c7..660a338 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -25,7 +25,7 @@ /** \mainpage Vulkan Memory Allocator -Version 1.0.0 (2017-06-16) +Version 2.0.0-alpha.1 (2017-07-04) Members grouped: see Modules. @@ -44,8 +44,6 @@ reasons: binding cannot be changed later - resource must be recreated. - Driver must be queried for supported memory heaps and memory types. Different IHV-s provide different types of it. -- Resources that don't fit in VRAM are not automatically evicted to RAM. - Developer must handle out-of-memory errors on his own. - It is recommended practice to allocate bigger chunks of memory and assign parts of them to particular resources. @@ -122,23 +120,17 @@ When you want to create a buffer or image: VkBuffer buffer; vmaCreateBuffer(allocator, &bufferInfo, &memReq, &buffer, nullptr, nullptr); -When no longer needed, destroy your buffer or image using vmaDestroyBuffer() / vmaDestroyImage(). -This function would also free memory bound to it. - - - vmaDestroyBuffer(allocator, buffer); - \section configuration Configuration Set VMA_STATS_STRING_ENABLED macro in vk_mem_alloc.h to 0 or 1 to disable/enable compilation of code for dumping internal allocator state to string in JSON format. -Please check "CONFIGURATION" section in vk_mem_alloc.cpp file to find macros and -other definitions that you can change to connect the library to your own -implementation of basic facilities like assert, min and max functions, mutex -etc. C++ STL is used by default, but changing these allows you to get rid of any -STL usage if you want, as many game developers tend to do. +Please check "CONFIGURATION SECTION" below to find macros and other definitions +that you can change to connect the library to your own implementation of basic +facilities like assert, min and max functions, mutex etc. C++ STL is used by +default, but changing these allows you to get rid of any STL usage if you want, +as many game developers tend to do. \section custom_memory_allocator Custom memory allocator @@ -161,6 +153,31 @@ call from multiple threads simultaneously, synchronized internally when needed. VK_DEFINE_HANDLE(VmaAllocator) +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator allocator, + uint32_t memoryType, + VkDeviceMemory memory, + VkDeviceSize size); +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator allocator, + uint32_t memoryType, + VkDeviceMemory memory, + VkDeviceSize size); + +/** \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. +*/ +typedef struct VmaDeviceMemoryCallbacks { + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction pfnFree; +} VmaDeviceMemoryCallbacks; + /// Description of a Allocator to be created. typedef struct VmaAllocatorCreateInfo { @@ -179,6 +196,9 @@ typedef struct VmaAllocatorCreateInfo /// Custom allocation callbacks. /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ const VkAllocationCallbacks* pAllocationCallbacks; + /// Informative callbacks for vkAllocateMemory, vkFreeMemory. + /** Optional, can be null. */ + const VmaDeviceMemoryCallbacks* pDeviceMemoryCallbacks; } VmaAllocatorCreateInfo; /// Creates Allocator object. @@ -273,17 +293,20 @@ typedef enum VmaMemoryUsage /// Memory will be used on device only, no need to be mapped on host. VMA_MEMORY_USAGE_GPU_ONLY = 1, /// Memory will be mapped on host. Could be used for transfer to device. + /** Guarantees to be HOST_VISIBLE and HOST_COHERENT. */ VMA_MEMORY_USAGE_CPU_ONLY = 2, /// Memory will be used for frequent (dynamic) updates from host and reads on device. + /** Guarantees to be HOST_VISIBLE. */ VMA_MEMORY_USAGE_CPU_TO_GPU = 3, /// Memory will be used for writing on device and readback on host. + /** Guarantees to be HOST_VISIBLE. */ VMA_MEMORY_USAGE_GPU_TO_CPU = 4, VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF } VmaMemoryUsage; -typedef struct VmaMemoryRequirements -{ - /** \brief Set to true if this allocation should have its own memory block. +/// Flags to be passed as VmaMemoryRequirements::flags. +typedef enum VmaMemoryRequirementFlagBits { + /** \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. @@ -291,7 +314,31 @@ typedef struct VmaMemoryRequirements simultaneously because otherwise they might end up as regions of the same VkDeviceMemory, while mapping same VkDeviceMemory multiple times is illegal. */ - VkBool32 ownMemory; + VMA_MEMORY_REQUIREMENT_OWN_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. + + It makes no sense to set VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT and + VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT at the same time. */ + VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through ppMappedData. You cannot + map the memory on your own as multiple maps of a single VkDeviceMemory are + illegal. + */ + VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT = 0x00000004, + + VMA_MEMORY_REQUIREMENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryRequirementFlagBits; +typedef VkFlags VmaMemoryRequirementFlags; + +typedef struct VmaMemoryRequirements +{ + VmaMemoryRequirementFlags flags; /** \brief Intended usage of memory. Leave VMA_MEMORY_USAGE_UNKNOWN if you specify requiredFlags. You can also use both. @@ -306,13 +353,8 @@ typedef struct VmaMemoryRequirements Set to 0 if no additional flags are prefered and only requiredFlags should be used. If not 0, it must be a superset or equal to requiredFlags. */ VkMemoryPropertyFlags preferredFlags; - /** \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. - - It makes no sense to set ownMemory and neverAllocate at the same time. */ - VkBool32 neverAllocate; + /** \brief Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). */ + void* pUserData; } VmaMemoryRequirements; /** @@ -342,15 +384,53 @@ VkResult vmaFindMemoryTypeIndex( @{ */ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \brief 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 call to vmaDefragment() if this allocation is passed to the function. + */ + VkDeviceMemory deviceMemory; + /** \brief Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation. + + It can change after call to vmaDefragment() if this allocation is passed to the function. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. Null if this alloaction is not persistently mapped. + + It can change after call to vmaUnmapPersistentlyMappedMemory(), vmaMapPersistentlyMappedMemory(). + It can also change after call to vmaDefragment() if this allocation is passed to the function. + */ + void* pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaMemoryRequirements::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* pUserData; +} VmaAllocationInfo; + /** \brief General purpose memory allocation. -@param[out] pMemory Allocated memory. -@param[out] pMemoryTypeIndex Optional. Index of memory type that has been chosen for this allocation. +@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(). -All allocated memory is also automatically freed in vmaDestroyAllocator(). - It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), vmaCreateBuffer(), vmaCreateImage() instead whenever possible. */ @@ -358,49 +438,190 @@ VkResult vmaAllocateMemory( VmaAllocator allocator, const VkMemoryRequirements* pVkMemoryRequirements, const VmaMemoryRequirements* pVmaMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); /** -@param[out] pMemoryTypeIndex Optional. Pass null if you don't need this information. +@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(). - -All allocated memory is also automatically freed in vmaDestroyAllocator(). */ VkResult vmaAllocateMemoryForBuffer( VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements* pMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); /// Function similar to vmaAllocateMemoryForBuffer(). VkResult vmaAllocateMemoryForImage( VmaAllocator allocator, VkImage image, const VmaMemoryRequirements* pMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); -/// Frees memory previously allocated using vmaAllocateMemoryForBuffer() or vmaAllocateMemoryForImage(). +/// Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). void vmaFreeMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory); + VmaAllocation allocation); + +/// Returns current information about specified allocation. +void vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo); + +/// Sets pUserData in given allocation to new value. +void vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData); /** Feel free to use vkMapMemory on these memory blocks on you own if you want, but just for convenience and to make sure correct offset and size is always specified, usage of vmaMapMemory() / vmaUnmapMemory() is recommended. + +Do not use it on memory allocated with VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT +as multiple maps to same VkDeviceMemory is illegal. */ VkResult vmaMapMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory, + VmaAllocation allocation, void** ppData); void vmaUnmapMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory); + VmaAllocation allocation); + +/** \brief Unmaps persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. + +This is optional performance optimization. You should call it on Windows for +time of call to vkQueueSubmit and vkQueuePresent, for performance reasons, +because of the internal behavior of WDDM. + +After this call VmaAllocationInfo::pMappedData of some allocations may become null. + +This call is reference-counted. Memory is mapped again after you call +vmaMapPersistentlyMappedMemory() same number of times that you called +vmaUnmapPersistentlyMappedMemory(). +*/ +void vmaUnmapPersistentlyMappedMemory(VmaAllocator allocator); + +/** \brief Maps back persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. + +See vmaUnmapPersistentlyMappedMemory(). + +After this call VmaAllocationInfo::pMappedData of some allocation may have value +different than before calling vmaUnmapPersistentlyMappedMemory(). +*/ +VkResult vmaMapPersistentlyMappedMemory(VmaAllocator allocator); + +/** \brief Optional configuration parameters to be passed to function vmaDefragment(). */ +typedef struct VmaDefragmentationInfo { + /** \brief Maximum total numbers of bytes that can be copied while moving allocations to different places. + + Default is VK_WHOLE_SIZE, which means no limit. + */ + VkDeviceSize maxBytesToMove; + /** \brief Maximum number of allocations that can be moved to different place. + + Default is UINT_MAX, which means no limit. + */ + uint32_t maxAllocationsToMove; +} VmaDefragmentationInfo; + +/** \brief Statistics returned by function vmaDefragment(). */ +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; + +/** \brief Compacts memory by moving allocations. + +@param pAllocations Array of allocations that can be moved during this compation. +@param allocationCount Number of elements in pAllocations and pAllocationsChanged arrays. +@param[out] pAllocationsChanged Array of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information. +@param pDefragmentationInfo Configuration parameters. Optional - pass null to use default values. +@param[out] pDefragmentationStats Statistics returned by the function. Optional - pass null if you don't need this information. +@return VK_SUCCESS if completed, VK_INCOMPLETE if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo have been reached, negative error code in case of error. + +This function works by moving allocations to different places (different +VkDeviceMemory objects and/or different offsets) in order to optimize memory +usage. Only allocations that are in pAllocations array can be moved. All other +allocations are considered nonmovable in this call. Basic rules: + +- Only allocations made in memory types that have + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT flag can be compacted. You may pass other + allocations but it makes no sense - these will never be moved. +- You may pass allocations made with VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT but + it makes no sense - they will never be moved. +- Both allocations made with or without VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT + flag can be compacted. If not persistently mapped, memory will be mapped + temporarily inside this function if needed, so it shouldn't be mapped by you for + the time of this call. +- You must not pass same VmaAllocation object multiple times in pAllocations array. + +The function also frees empty VkDeviceMemory blocks. + +After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or +VmaAllocationInfo::offset changes. You must query them again using +vmaGetAllocationInfo() if you need them. + +If an allocation has been moved, data in memory is copied to new place +automatically, but if it was bound to a buffer or an image, you must destroy +that object yourself, create new one and bind it to the new memory pointed by +the allocation. You must use vkDestroyBuffer(), vkDestroyImage(), +vkCreateBuffer(), vkCreateImage() for that purpose and NOT vmaDestroyBuffer(), +vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example: + + + VkDevice device = ...; + VmaAllocator allocator = ...; + std::vector buffers = ...; + std::vector allocations = ...; + + std::vector allocationsChanged(allocations.size()); + vmaDefragment(allocator, allocations.data(), allocations.size(), allocationsChanged.data(), nullptr, nullptr); + + for(size_t i = 0; i < allocations.size(); ++i) + { + if(allocationsChanged[i]) + { + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, allocations[i], &allocInfo); + + vkDestroyBuffer(device, buffers[i], nullptr); + + VkBufferCreateInfo bufferInfo = ...; + vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]); + + .// You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning. + + vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset); + } + } + +This function may be time-consuming, so you shouldn't call it too often (like +every frame or after every resource creation/destruction), but rater you can +call it on special occasions (like when reloading a game level, when you just +destroyed a lot of objects). +*/ +VkResult vmaDefragment( + VmaAllocator allocator, + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo *pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats); /** @} */ @@ -410,8 +631,9 @@ void vmaUnmapMemory( */ /** -@param[out] pMemory Optional. Pass null if you don't need this information. -@param[out] pMemoryTypeIndex Optional. Pass null if you don't need this information. +@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: @@ -422,21 +644,19 @@ This function automatically: You do not (and should not) pass returned pMemory to vmaFreeMemory. Only calling vmaDestroyBuffer() / vmaDestroyImage() is required for objects created using vmaCreateBuffer() / vmaCreateImage(). - -All allocated buffers and images are also automatically destroyed in -vmaDestroyAllocator(), along with their memory allocations. */ VkResult vmaCreateBuffer( VmaAllocator allocator, const VkBufferCreateInfo* pCreateInfo, const VmaMemoryRequirements* pMemoryRequirements, VkBuffer* pBuffer, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); void vmaDestroyBuffer( VmaAllocator allocator, - VkBuffer buffer); + VkBuffer buffer, + VmaAllocation allocation); /// Function similar to vmaCreateBuffer(). VkResult vmaCreateImage( @@ -444,21 +664,25 @@ VkResult vmaCreateImage( const VkImageCreateInfo* pCreateInfo, const VmaMemoryRequirements* pMemoryRequirements, VkImage* pImage, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo); void vmaDestroyImage( VmaAllocator allocator, - VkImage image); + VkImage image, + VmaAllocation allocation); /** @} */ +#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H + #ifdef VMA_IMPLEMENTATION +#undef VMA_IMPLEMENTATION #include /******************************************************************************* -CONFIGURATION +CONFIGURATION SECTION Change these definitions depending on your environment. */ @@ -519,6 +743,9 @@ remove them if not needed. #define VMA_MAX(v1, v2) (std::max((v1), (v2))) #define VMA_SWAP(v1, v2) std::swap((v1), (v2)) +// You can just comment this out to use internal sorting implementation. +//#define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) + #define VMA_DEBUG_LOG(format, ...) /* #define VMA_DEBUG_LOG(format, ...) do { \ @@ -634,6 +861,47 @@ inline T VmaRoundDiv(T x, T y) { return (x + (y / (T)2)) / y; } + +#ifndef VMA_SORT + +template +Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp) +{ + Iterator centerValue = end; --centerValue; + Iterator insertIndex = beg; + for(Iterator i = beg; i < centerValue; ++i) + { + if(cmp(*i, *centerValue)) + { + if(insertIndex != i) + { + VMA_SWAP(*i, *insertIndex); + } + ++insertIndex; + } + } + if(insertIndex != centerValue) + { + VMA_SWAP(*insertIndex, *centerValue); + } + return insertIndex; +} + +template +void VmaQuickSort(Iterator beg, Iterator end, Compare cmp) +{ + if(beg < end) + { + Iterator it = VmaQuickSortPartition(beg, end, cmp); + VmaQuickSort(beg, it, cmp); + VmaQuickSort(it + 1, end, cmp); + } +} + +#define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp) + +#endif // #ifndef VMA_SORT + /* Returns true if two memory blocks occupy overlapping pages. ResourceA must be in less memory offset than ResourceB. @@ -868,7 +1136,7 @@ template class VmaVector { public: - VmaVector(AllocatorT& allocator) : + VmaVector(const AllocatorT& allocator) : m_Allocator(allocator), m_pArray(VMA_NULL), m_Count(0), @@ -876,7 +1144,7 @@ public: { } - VmaVector(size_t count, AllocatorT& allocator) : + VmaVector(size_t count, const AllocatorT& allocator) : m_Allocator(allocator), m_pArray(count ? (T*)VmaAllocateArray(allocator->m_pCallbacks, count) : VMA_NULL), m_Count(count), @@ -1620,7 +1888,7 @@ public: friend class VmaList; }; - VmaList(AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { } + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { } bool empty() const { return m_RawList.IsEmpty(); } size_t size() const { return m_RawList.GetCount(); } @@ -1674,7 +1942,7 @@ public: typedef VmaPair PairType; typedef PairType* iterator; - VmaMap(VmaStlAllocator& allocator) : m_Vector(allocator) { } + VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) { } iterator begin() { return m_Vector.begin(); } iterator end() { return m_Vector.end(); } @@ -1735,8 +2003,160 @@ void VmaMap::erase(iterator it) #endif // #if VMA_USE_STL_UNORDERED_MAP +//////////////////////////////////////////////////////////////////////////////// + +class VmaBlock; + +enum VMA_BLOCK_VECTOR_TYPE +{ + VMA_BLOCK_VECTOR_TYPE_UNMAPPED, + VMA_BLOCK_VECTOR_TYPE_MAPPED, + VMA_BLOCK_VECTOR_TYPE_COUNT +}; + +static VMA_BLOCK_VECTOR_TYPE VmaMemoryRequirementFlagsToBlockVectorType(VmaMemoryRequirementFlags flags) +{ + return (flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0 ? + VMA_BLOCK_VECTOR_TYPE_MAPPED : + VMA_BLOCK_VECTOR_TYPE_UNMAPPED; +} + +struct VmaAllocation_T +{ +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_OWN, + }; + + VmaAllocation_T() + { + memset(this, 0, sizeof(VmaAllocation_T)); + } + + void InitBlockAllocation( + VmaBlock* block, + VkDeviceSize offset, + VkDeviceSize alignment, + VkDeviceSize size, + VmaSuballocationType suballocationType, + void* pUserData) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_pUserData = pUserData; + m_SuballocationType = suballocationType; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_Offset = offset; + } + + void ChangeBlockAllocation( + VmaBlock* block, + VkDeviceSize offset) + { + VMA_ASSERT(block != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_Offset = offset; + } + + void InitOwnAllocation( + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + bool persistentMap, + void* pMappedData, + VkDeviceSize size, + void* pUserData) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = ALLOCATION_TYPE_OWN; + m_Alignment = 0; + m_Size = size; + m_pUserData = pUserData; + m_SuballocationType = suballocationType; + m_OwnAllocation.m_MemoryTypeIndex = memoryTypeIndex; + m_OwnAllocation.m_hMemory = hMemory; + m_OwnAllocation.m_PersistentMap = persistentMap; + m_OwnAllocation.m_pMappedData = pMappedData; + } + + ALLOCATION_TYPE GetType() const { return m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + void* GetUserData() const { return m_pUserData; } + void SetUserData(void* pUserData) { m_pUserData = pUserData; } + VmaSuballocationType GetSuballocationType() const { return m_SuballocationType; } + + VmaBlock* GetBlock() const + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + return m_BlockAllocation.m_Block; + } + VkDeviceSize GetOffset() const + { + return (m_Type == ALLOCATION_TYPE_BLOCK) ? m_BlockAllocation.m_Offset : 0; + } + VkDeviceMemory GetMemory() const; + uint32_t GetMemoryTypeIndex() const; + VMA_BLOCK_VECTOR_TYPE GetBlockVectorType() const; + void* GetMappedData() const; + + VkResult OwnAllocMapPersistentlyMappedMemory(VkDevice hDevice) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_OWN); + if(m_OwnAllocation.m_PersistentMap) + { + return vkMapMemory(hDevice, m_OwnAllocation.m_hMemory, 0, VK_WHOLE_SIZE, 0, &m_OwnAllocation.m_pMappedData); + } + return VK_SUCCESS; + } + void OwnAllocUnmapPersistentlyMappedMemory(VkDevice hDevice) + { + VMA_ASSERT(m_Type == ALLOCATION_TYPE_OWN); + if(m_OwnAllocation.m_pMappedData) + { + VMA_ASSERT(m_OwnAllocation.m_PersistentMap); + vkUnmapMemory(hDevice, m_OwnAllocation.m_hMemory); + m_OwnAllocation.m_pMappedData = VMA_NULL; + } + } + +private: + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + ALLOCATION_TYPE m_Type; + VmaSuballocationType m_SuballocationType; + + union + { + // Allocation out of VmaBlock. + struct BlockAllocation + { + VmaBlock* m_Block; + VkDeviceSize m_Offset; + } m_BlockAllocation; + + // Allocation for an object that has its own private VkDeviceMemory. + struct OwnAllocation + { + uint32_t m_MemoryTypeIndex; + VkDeviceMemory m_hMemory; + bool m_PersistentMap; + void* m_pMappedData; + } m_OwnAllocation; + }; +}; + /* -Represents a region of VmaAllocation that is either assigned and returned as +Represents a region of VmaBlock that is either assigned and returned as allocated memory block or free. */ struct VmaSuballocation @@ -1757,11 +2177,15 @@ struct VmaAllocationRequest /* Single block of memory - VkDeviceMemory with all the data about its regions assigned or free. */ -class VmaAllocation +class VmaBlock { public: + uint32_t m_MemoryTypeIndex; + VMA_BLOCK_VECTOR_TYPE m_BlockVectorType; VkDeviceMemory m_hMemory; VkDeviceSize m_Size; + bool m_PersistentMap; + void* m_pMappedData; uint32_t m_FreeCount; VkDeviceSize m_SumFreeSize; VmaSuballocationList m_Suballocations; @@ -1769,15 +2193,21 @@ public: // Sorted by size, ascending. VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize; - VmaAllocation(VmaAllocator hAllocator); + VmaBlock(VmaAllocator hAllocator); - ~VmaAllocation() + ~VmaBlock() { VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); } // Always call after construction. - void Init(VkDeviceMemory newMemory, VkDeviceSize newSize); + void Init( + uint32_t newMemoryTypeIndex, + VMA_BLOCK_VECTOR_TYPE newBlockVectorType, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + bool persistentMap, + void* pMappedData); // Always call before destruction. void Destroy(VmaAllocator allocator); @@ -1815,7 +2245,7 @@ public: VkDeviceSize allocSize); // Frees suballocation assigned to given memory region. - void Free(const VkMappedMemoryRange* pMemory); + void Free(const VmaAllocation allocation); #if VMA_STATS_STRING_ENABLED void PrintDetailedMap(class VmaStringBuilder& sb) const; @@ -1835,53 +2265,43 @@ private: void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); }; -// Allocation for an object that has its own private VkDeviceMemory. -struct VmaOwnAllocation +struct VmaPointerLess { - VkDeviceMemory m_hMemory; - VkDeviceSize m_Size; - VmaSuballocationType m_Type; -}; - -struct VmaOwnAllocationMemoryHandleLess -{ - bool operator()(const VmaOwnAllocation& lhs, const VmaOwnAllocation& rhs) const + bool operator()(const void* lhs, const void* rhs) const { - return lhs.m_hMemory < rhs.m_hMemory; - } - bool operator()(const VmaOwnAllocation& lhs, VkDeviceMemory rhsMem) const - { - return lhs.m_hMemory < rhsMem; + return lhs < rhs; } }; -/* Sequence of VmaAllocation. Represents memory blocks allocated for a specific +/* Sequence of VmaBlock. Represents memory blocks allocated for a specific Vulkan memory type. */ -struct VmaAllocationVector +struct VmaBlockVector { // Incrementally sorted by sumFreeSize, ascending. - VmaVector< VmaAllocation*, VmaStlAllocator > m_Allocations; + VmaVector< VmaBlock*, VmaStlAllocator > m_Blocks; - VmaAllocationVector(VmaAllocator hAllocator); - ~VmaAllocationVector(); + VmaBlockVector(VmaAllocator hAllocator); + ~VmaBlockVector(); - bool IsEmpty() const { return m_Allocations.empty(); } + bool IsEmpty() const { return m_Blocks.empty(); } - // Tries to free memory from any if its Allocations. - // Returns index of Allocation that the memory was freed from, or -1 if not found. - size_t Free(const VkMappedMemoryRange* pMemory); + // Finds and removes given block from vector. + void Remove(VmaBlock* pBlock); - // Performs single step in sorting m_Allocations. They may not be fully sorted + // Performs single step in sorting m_Blocks. They may not be fully sorted // after this call. - void IncrementallySortAllocations(); + void IncrementallySortBlocks(); - // Adds statistics of this AllocationVector to pStats. + // Adds statistics of this BlockVector to pStats. void AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const; #if VMA_STATS_STRING_ENABLED void PrintDetailedMap(class VmaStringBuilder& sb) const; #endif + void UnmapPersistentlyMappedMemory(); + VkResult MapPersistentlyMappedMemory(); + private: VmaAllocator m_hAllocator; }; @@ -1892,31 +2312,28 @@ struct VmaAllocator_T VkDevice m_hDevice; bool m_AllocationCallbacksSpecified; VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; VkDeviceSize m_PreferredLargeHeapBlockSize; VkDeviceSize m_PreferredSmallHeapBlockSize; + // Non-zero when we are inside UnmapPersistentlyMappedMemory...MapPersistentlyMappedMemory. + // Counter to allow nested calls to these functions. + uint32_t m_UnmapPersistentlyMappedMemoryCounter; VkPhysicalDeviceProperties m_PhysicalDeviceProperties; VkPhysicalDeviceMemoryProperties m_MemProps; - VmaAllocationVector* m_pAllocations[VK_MAX_MEMORY_TYPES]; + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT]; /* There can be at most one allocation that is completely empty - a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */ - bool m_HasEmptyAllocation[VK_MAX_MEMORY_TYPES]; - VmaMutex m_AllocationsMutex[VK_MAX_MEMORY_TYPES]; + bool m_HasEmptyBlock[VK_MAX_MEMORY_TYPES]; + VmaMutex m_BlocksMutex[VK_MAX_MEMORY_TYPES]; // Each vector is sorted by memory (handle value). - typedef VmaVector< VmaOwnAllocation, VmaStlAllocator > OwnAllocationVectorType; - OwnAllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES]; + typedef VmaVector< VmaAllocation, VmaStlAllocator > AllocationVectorType; + AllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT]; VmaMutex m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES]; - // Sorted by first (VkBuffer handle value). - VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange) m_BufferToMemoryMap; - VmaMutex m_BufferToMemoryMapMutex; - // Sorted by first (VkImage handle value). - VMA_MAP_TYPE(VkImage, VkMappedMemoryRange) m_ImageToMemoryMap; - VmaMutex m_ImageToMemoryMapMutex; - VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); ~VmaAllocator_T(); @@ -1942,11 +2359,10 @@ struct VmaAllocator_T const VkMemoryRequirements& vkMemReq, const VmaMemoryRequirements& vmaMemReq, VmaSuballocationType suballocType, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex); + VmaAllocation* pAllocation); // Main deallocation function. - void FreeMemory(const VkMappedMemoryRange* pMemory); + void FreeMemory(const VmaAllocation allocation); void CalculateStats(VmaStats* pStats); @@ -1954,6 +2370,18 @@ struct VmaAllocator_T void PrintDetailedMap(class VmaStringBuilder& sb); #endif + void UnmapPersistentlyMappedMemory(); + VkResult MapPersistentlyMappedMemory(); + + VkResult Defragment( + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo* pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats); + + static void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + private: VkPhysicalDevice m_PhysicalDevice; @@ -1962,17 +2390,19 @@ private: const VmaMemoryRequirements& vmaMemReq, uint32_t memTypeIndex, VmaSuballocationType suballocType, - VkMappedMemoryRange* pMemory); + VmaAllocation* pAllocation); // Allocates and registers new VkDeviceMemory specifically for single allocation. VkResult AllocateOwnMemory( VkDeviceSize size, VmaSuballocationType suballocType, uint32_t memTypeIndex, - VkMappedMemoryRange* pMemory); + bool map, + void* pUserData, + VmaAllocation* pAllocation); // Tries to free pMemory as Own Memory. Returns true if found and freed. - bool FreeOwnMemory(const VkMappedMemoryRange* pMemory); + void FreeOwnMemory(VmaAllocation allocation); }; //////////////////////////////////////////////////////////////////////////////// @@ -2105,6 +2535,47 @@ void VmaStringBuilder::AddString(const char* pStr) //////////////////////////////////////////////////////////////////////////////// +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + return (m_Type == ALLOCATION_TYPE_BLOCK) ? + m_BlockAllocation.m_Block->m_hMemory : m_OwnAllocation.m_hMemory; +} + +uint32_t VmaAllocation_T::GetMemoryTypeIndex() const +{ + return (m_Type == ALLOCATION_TYPE_BLOCK) ? + m_BlockAllocation.m_Block->m_MemoryTypeIndex : m_OwnAllocation.m_MemoryTypeIndex; +} + +VMA_BLOCK_VECTOR_TYPE VmaAllocation_T::GetBlockVectorType() const +{ + return (m_Type == ALLOCATION_TYPE_BLOCK) ? + m_BlockAllocation.m_Block->m_BlockVectorType : + (m_OwnAllocation.m_PersistentMap ? VMA_BLOCK_VECTOR_TYPE_MAPPED : VMA_BLOCK_VECTOR_TYPE_UNMAPPED); +} + +void* VmaAllocation_T::GetMappedData() const +{ + switch(m_Type) + { + case ALLOCATION_TYPE_BLOCK: + if(m_BlockAllocation.m_Block->m_pMappedData != VMA_NULL) + { + return (char*)m_BlockAllocation.m_Block->m_pMappedData + m_BlockAllocation.m_Offset; + } + else + { + return VMA_NULL; + } + break; + case ALLOCATION_TYPE_OWN: + return m_OwnAllocation.m_pMappedData; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + // Correspond to values of enum VmaSuballocationType. static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = { "FREE", @@ -2160,9 +2631,13 @@ struct VmaSuballocationItemSizeLess } }; -VmaAllocation::VmaAllocation(VmaAllocator hAllocator) : +VmaBlock::VmaBlock(VmaAllocator hAllocator) : + m_MemoryTypeIndex(UINT_MAX), + m_BlockVectorType(VMA_BLOCK_VECTOR_TYPE_COUNT), m_hMemory(VK_NULL_HANDLE), m_Size(0), + m_PersistentMap(false), + m_pMappedData(VMA_NULL), m_FreeCount(0), m_SumFreeSize(0), m_Suballocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), @@ -2170,12 +2645,22 @@ VmaAllocation::VmaAllocation(VmaAllocator hAllocator) : { } -void VmaAllocation::Init(VkDeviceMemory newMemory, VkDeviceSize newSize) +void VmaBlock::Init( + uint32_t newMemoryTypeIndex, + VMA_BLOCK_VECTOR_TYPE newBlockVectorType, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + bool persistentMap, + void* pMappedData) { VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + m_MemoryTypeIndex = newMemoryTypeIndex; + m_BlockVectorType = newBlockVectorType; m_hMemory = newMemory; m_Size = newSize; + m_PersistentMap = persistentMap; + m_pMappedData = pMappedData; m_FreeCount = 1; m_SumFreeSize = newSize; @@ -2193,14 +2678,26 @@ void VmaAllocation::Init(VkDeviceMemory newMemory, VkDeviceSize newSize) m_FreeSuballocationsBySize.push_back(suballocItem); } -void VmaAllocation::Destroy(VmaAllocator allocator) +void VmaBlock::Destroy(VmaAllocator allocator) { VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); + if(m_pMappedData != VMA_NULL) + { + vkUnmapMemory(allocator->m_hDevice, m_hMemory); + m_pMappedData = VMA_NULL; + } + + // Callback. + if(allocator->m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*allocator->m_DeviceMemoryCallbacks.pfnFree)(allocator, m_MemoryTypeIndex, m_hMemory, m_Size); + } + vkFreeMemory(allocator->m_hDevice, m_hMemory, allocator->GetAllocationCallbacks()); m_hMemory = VK_NULL_HANDLE; } -bool VmaAllocation::Validate() const +bool VmaBlock::Validate() const { if((m_hMemory == VK_NULL_HANDLE) || (m_Size == 0) || @@ -2285,7 +2782,7 @@ How many suitable free suballocations to analyze before choosing best one. */ //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8; -bool VmaAllocation::CreateAllocationRequest( +bool VmaBlock::CreateAllocationRequest( VkDeviceSize bufferImageGranularity, VkDeviceSize allocSize, VkDeviceSize allocAlignment, @@ -2377,7 +2874,7 @@ bool VmaAllocation::CreateAllocationRequest( return false; } -bool VmaAllocation::CheckAllocation( +bool VmaBlock::CheckAllocation( VkDeviceSize bufferImageGranularity, VkDeviceSize allocSize, VkDeviceSize allocAlignment, @@ -2472,12 +2969,12 @@ bool VmaAllocation::CheckAllocation( return true; } -bool VmaAllocation::IsEmpty() const +bool VmaBlock::IsEmpty() const { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); } -void VmaAllocation::Alloc( +void VmaBlock::Alloc( const VmaAllocationRequest& request, VmaSuballocationType type, VkDeviceSize allocSize) @@ -2535,7 +3032,7 @@ void VmaAllocation::Alloc( m_SumFreeSize -= allocSize; } -void VmaAllocation::FreeSuballocation(VmaSuballocationList::iterator suballocItem) +void VmaBlock::FreeSuballocation(VmaSuballocationList::iterator suballocItem) { // Change this suballocation to be marked as free. VmaSuballocation& suballoc = *suballocItem; @@ -2578,11 +3075,12 @@ void VmaAllocation::FreeSuballocation(VmaSuballocationList::iterator suballocIte RegisterFreeSuballocation(suballocItem); } -void VmaAllocation::Free(const VkMappedMemoryRange* pMemory) +void VmaBlock::Free(const VmaAllocation allocation) { // If suballocation to free has offset smaller than half of allocation size, search forward. // Otherwise search backward. - const bool forwardDirection = pMemory->offset < (m_Size / 2); + const VkDeviceSize allocationOffset = allocation->GetOffset(); + const bool forwardDirection = allocationOffset < (m_Size / 2); if(forwardDirection) { for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); @@ -2590,7 +3088,7 @@ void VmaAllocation::Free(const VkMappedMemoryRange* pMemory) ++suballocItem) { VmaSuballocation& suballoc = *suballocItem; - if(suballoc.offset == pMemory->offset) + if(suballoc.offset == allocationOffset) { FreeSuballocation(suballocItem); VMA_HEAVY_ASSERT(Validate()); @@ -2606,7 +3104,7 @@ void VmaAllocation::Free(const VkMappedMemoryRange* pMemory) ++suballocItem) { VmaSuballocation& suballoc = *suballocItem; - if(suballoc.offset == pMemory->offset) + if(suballoc.offset == allocationOffset) { FreeSuballocation(suballocItem); VMA_HEAVY_ASSERT(Validate()); @@ -2619,7 +3117,7 @@ void VmaAllocation::Free(const VkMappedMemoryRange* pMemory) #if VMA_STATS_STRING_ENABLED -void VmaAllocation::PrintDetailedMap(class VmaStringBuilder& sb) const +void VmaBlock::PrintDetailedMap(class VmaStringBuilder& sb) const { sb.Add("{\n\t\t\t\"Bytes\": "); sb.AddNumber(m_Size); @@ -2653,7 +3151,7 @@ void VmaAllocation::PrintDetailedMap(class VmaStringBuilder& sb) const #endif // #if VMA_STATS_STRING_ENABLED -void VmaAllocation::MergeFreeWithNext(VmaSuballocationList::iterator item) +void VmaBlock::MergeFreeWithNext(VmaSuballocationList::iterator item) { VMA_ASSERT(item != m_Suballocations.end()); VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); @@ -2668,7 +3166,7 @@ void VmaAllocation::MergeFreeWithNext(VmaSuballocationList::iterator item) m_Suballocations.erase(nextItem); } -void VmaAllocation::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +void VmaBlock::RegisterFreeSuballocation(VmaSuballocationList::iterator item) { VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); VMA_ASSERT(item->size > 0); @@ -2690,7 +3188,7 @@ void VmaAllocation::RegisterFreeSuballocation(VmaSuballocationList::iterator ite } } -void VmaAllocation::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) +void VmaBlock::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) { VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); VMA_ASSERT(item->size > 0); @@ -2724,7 +3222,7 @@ static void InitStatInfo(VmaStatInfo& outInfo) outInfo.UnusedRangeSizeMin = UINT64_MAX; } -static void CalcAllocationStatInfo(VmaStatInfo& outInfo, const VmaAllocation& alloc) +static void CalcAllocationStatInfo(VmaStatInfo& outInfo, const VmaBlock& alloc) { outInfo.AllocationCount = 1; @@ -2780,46 +3278,42 @@ static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo) VmaRoundDiv(inoutInfo.UnusedBytes, inoutInfo.UnusedRangeCount) : 0; } -VmaAllocationVector::VmaAllocationVector(VmaAllocator hAllocator) : +VmaBlockVector::VmaBlockVector(VmaAllocator hAllocator) : m_hAllocator(hAllocator), - m_Allocations(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())) { } -VmaAllocationVector::~VmaAllocationVector() +VmaBlockVector::~VmaBlockVector() { - for(size_t i = m_Allocations.size(); i--; ) + for(size_t i = m_Blocks.size(); i--; ) { - m_Allocations[i]->Destroy(m_hAllocator); - vma_delete(m_hAllocator, m_Allocations[i]); + m_Blocks[i]->Destroy(m_hAllocator); + vma_delete(m_hAllocator, m_Blocks[i]); } } -size_t VmaAllocationVector::Free(const VkMappedMemoryRange* pMemory) +void VmaBlockVector::Remove(VmaBlock* pBlock) { - for(uint32_t allocIndex = 0; allocIndex < m_Allocations.size(); ++allocIndex) + for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) { - VmaAllocation* const pAlloc = m_Allocations[allocIndex]; - VMA_ASSERT(pAlloc); - if(pAlloc->m_hMemory == pMemory->memory) + if(m_Blocks[blockIndex] == pBlock) { - pAlloc->Free(pMemory); - VMA_HEAVY_ASSERT(pAlloc->Validate()); - return allocIndex; + VectorRemove(m_Blocks, blockIndex); + return; } } - - return (size_t)-1; + VMA_ASSERT(0); } -void VmaAllocationVector::IncrementallySortAllocations() +void VmaBlockVector::IncrementallySortBlocks() { // Bubble sort only until first swap. - for(size_t i = 1; i < m_Allocations.size(); ++i) + for(size_t i = 1; i < m_Blocks.size(); ++i) { - if(m_Allocations[i - 1]->m_SumFreeSize > m_Allocations[i]->m_SumFreeSize) + if(m_Blocks[i - 1]->m_SumFreeSize > m_Blocks[i]->m_SumFreeSize) { - VMA_SWAP(m_Allocations[i - 1], m_Allocations[i]); + VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); return; } } @@ -2827,35 +3321,471 @@ void VmaAllocationVector::IncrementallySortAllocations() #if VMA_STATS_STRING_ENABLED -void VmaAllocationVector::PrintDetailedMap(class VmaStringBuilder& sb) const +void VmaBlockVector::PrintDetailedMap(class VmaStringBuilder& sb) const { - for(size_t i = 0; i < m_Allocations.size(); ++i) + for(size_t i = 0; i < m_Blocks.size(); ++i) { if(i > 0) sb.Add(",\n\t\t"); else sb.Add("\n\t\t"); - m_Allocations[i]->PrintDetailedMap(sb); + m_Blocks[i]->PrintDetailedMap(sb); } } #endif // #if VMA_STATS_STRING_ENABLED -void VmaAllocationVector::AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const +void VmaBlockVector::UnmapPersistentlyMappedMemory() { - for(uint32_t allocIndex = 0; allocIndex < m_Allocations.size(); ++allocIndex) + for(size_t i = m_Blocks.size(); i--; ) { - const VmaAllocation* const pAlloc = m_Allocations[allocIndex]; - VMA_ASSERT(pAlloc); - VMA_HEAVY_ASSERT(pAlloc->Validate()); + VmaBlock* pBlock = m_Blocks[i]; + if(pBlock->m_pMappedData != VMA_NULL) + { + VMA_ASSERT(pBlock->m_PersistentMap != false); + vkUnmapMemory(m_hAllocator->m_hDevice, pBlock->m_hMemory); + pBlock->m_pMappedData = VMA_NULL; + } + } +} + +VkResult VmaBlockVector::MapPersistentlyMappedMemory() +{ + VkResult finalResult = VK_SUCCESS; + for(size_t i = 0, count = m_Blocks.size(); i < count; ++i) + { + VmaBlock* pBlock = m_Blocks[i]; + if(pBlock->m_PersistentMap) + { + VMA_ASSERT(pBlock->m_pMappedData == nullptr); + VkResult localResult = vkMapMemory(m_hAllocator->m_hDevice, pBlock->m_hMemory, 0, VK_WHOLE_SIZE, 0, &pBlock->m_pMappedData); + if(localResult != VK_SUCCESS) + { + finalResult = localResult; + } + } + } + return finalResult; +} + +void VmaBlockVector::AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const +{ + for(uint32_t allocIndex = 0; allocIndex < m_Blocks.size(); ++allocIndex) + { + const VmaBlock* const pBlock = m_Blocks[allocIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); VmaStatInfo allocationStatInfo; - CalcAllocationStatInfo(allocationStatInfo, *pAlloc); + CalcAllocationStatInfo(allocationStatInfo, *pBlock); VmaAddStatInfo(pStats->total, allocationStatInfo); VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo); VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo); } } +//////////////////////////////////////////////////////////////////////////////// +// VmaDefragmentator + +class VmaDefragmentator +{ + VkDevice m_hDevice; + const VkAllocationCallbacks* m_pAllocationCallbacks; + VkDeviceSize m_BufferImageGranularity; + uint32_t m_MemTypeIndex; + VMA_BLOCK_VECTOR_TYPE m_BlockVectorType; + VkDeviceSize m_BytesMoved; + uint32_t m_AllocationsMoved; + + struct AllocationInfo + { + VmaAllocation m_hAllocation; + VkBool32* m_pChanged; + + AllocationInfo() : + m_hAllocation(VK_NULL_HANDLE), + m_pChanged(VMA_NULL) + { + } + }; + + struct AllocationInfoSizeGreater + { + bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const + { + return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize(); + } + }; + + // Used between AddAllocation and Defragment. + VmaVector< AllocationInfo, VmaStlAllocator > m_Allocations; + + struct BlockInfo + { + VmaBlock* m_pBlock; + bool m_HasNonMovableAllocations; + VmaVector< AllocationInfo, VmaStlAllocator > m_Allocations; + + BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) : + m_pBlock(VMA_NULL), + m_HasNonMovableAllocations(true), + m_Allocations(pAllocationCallbacks), + m_pMappedDataForDefragmentation(VMA_NULL) + { + } + + void CalcHasNonMovableAllocations() + { + const size_t blockAllocCount = + m_pBlock->m_Suballocations.size() - m_pBlock->m_FreeCount; + const size_t defragmentAllocCount = m_Allocations.size(); + m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount; + } + + void SortAllocationsBySizeDescecnding() + { + VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater()); + } + + VkResult EnsureMapping(VkDevice hDevice, void** ppMappedData) + { + // It has already been mapped for defragmentation. + if(m_pMappedDataForDefragmentation) + { + *ppMappedData = m_pMappedDataForDefragmentation; + return VK_SUCCESS; + } + + // It is persistently mapped. + if(m_pBlock->m_PersistentMap) + { + VMA_ASSERT(m_pBlock->m_pMappedData != VMA_NULL); + *ppMappedData = m_pBlock->m_pMappedData; + return VK_SUCCESS; + } + + // Map on first usage. + VkResult res = vkMapMemory(hDevice, m_pBlock->m_hMemory, 0, VK_WHOLE_SIZE, 0, &m_pMappedDataForDefragmentation); + *ppMappedData = m_pMappedDataForDefragmentation; + return res; + } + + void Unmap(VkDevice hDevice) + { + if(m_pMappedDataForDefragmentation != VMA_NULL) + { + vkUnmapMemory(hDevice, m_pBlock->m_hMemory); + } + } + + private: + // Not null if mapped for defragmentation only, not persistently mapped. + void* m_pMappedDataForDefragmentation; + }; + + struct BlockPointerLess + { + bool operator()(const BlockInfo* pLhsBlockInfo, const VmaBlock* pRhsBlock) const + { + return pLhsBlockInfo->m_pBlock < pRhsBlock; + } + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const + { + return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock; + } + }; + + // 1. Blocks with some non-movable allocations go first. + // 2. Blocks with smaller sumFreeSize go first. + struct BlockInfoCompareMoveDestination + { + bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const + { + if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations) + return true; + if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations) + return false; + if(pLhsBlockInfo->m_pBlock->m_SumFreeSize < pRhsBlockInfo->m_pBlock->m_SumFreeSize) + return true; + return false; + } + }; + + typedef VmaVector< BlockInfo*, VmaStlAllocator > BlockInfoVector; + BlockInfoVector m_Blocks; + + VkResult DefragmentRound( + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove); + + static bool MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset); + +public: + VmaDefragmentator( + VkDevice hDevice, + const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, + uint32_t memTypeIndex, + VMA_BLOCK_VECTOR_TYPE blockVectorType); + + ~VmaDefragmentator(); + + VkDeviceSize GetBytesMoved() const { return m_BytesMoved; } + uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; } + + void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); + + VkResult Defragment( + VmaBlockVector* pBlockVector, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove); +}; + +VmaDefragmentator::VmaDefragmentator( + VkDevice hDevice, + const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, + uint32_t memTypeIndex, + VMA_BLOCK_VECTOR_TYPE blockVectorType) : + m_hDevice(hDevice), + m_pAllocationCallbacks(pAllocationCallbacks), + m_BufferImageGranularity(bufferImageGranularity), + m_MemTypeIndex(memTypeIndex), + m_BlockVectorType(blockVectorType), + m_BytesMoved(0), + m_AllocationsMoved(0), + m_Allocations(VmaStlAllocator(pAllocationCallbacks)), + m_Blocks(VmaStlAllocator(pAllocationCallbacks)) +{ +} + +VmaDefragmentator::~VmaDefragmentator() +{ + for(size_t i = m_Blocks.size(); i--; ) + { + vma_delete(m_pAllocationCallbacks, m_Blocks[i]); + } +} + +void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged) +{ + AllocationInfo allocInfo; + allocInfo.m_hAllocation = hAlloc; + allocInfo.m_pChanged = pChanged; + m_Allocations.push_back(allocInfo); +} + +VkResult VmaDefragmentator::DefragmentRound( + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) +{ + if(m_Blocks.empty()) + { + return VK_SUCCESS; + } + + size_t srcBlockIndex = m_Blocks.size() - 1; + size_t srcAllocIndex = SIZE_MAX; + for(;;) + { + // 1. Find next allocation to move. + // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source". + // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest. + while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size()) + { + if(m_Blocks[srcBlockIndex]->m_Allocations.empty()) + { + // Finished: no more allocations to process. + if(srcBlockIndex == 0) + { + return VK_SUCCESS; + } + else + { + --srcBlockIndex; + srcAllocIndex = SIZE_MAX; + } + } + else + { + srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1; + } + } + + BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex]; + AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex]; + + const VkDeviceSize size = allocInfo.m_hAllocation->GetSize(); + const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset(); + const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment(); + const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType(); + + // 2. Try to find new place for this allocation in preceding or current block. + for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex) + { + BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex]; + VmaAllocationRequest dstAllocRequest; + if(pDstBlockInfo->m_pBlock->CreateAllocationRequest( + m_BufferImageGranularity, + size, + alignment, + suballocType, + &dstAllocRequest) && + MoveMakesSense( + dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset)) + { + // Reached limit on number of allocations or bytes to move. + if((m_AllocationsMoved + 1 > maxAllocationsToMove) || + (m_BytesMoved + size > maxBytesToMove)) + { + return VK_INCOMPLETE; + } + + void* pDstMappedData = VMA_NULL; + VkResult res = pDstBlockInfo->EnsureMapping(m_hDevice, &pDstMappedData); + if(res != VK_SUCCESS) + { + return res; + } + + void* pSrcMappedData = VMA_NULL; + res = pSrcBlockInfo->EnsureMapping(m_hDevice, &pSrcMappedData); + if(res != VK_SUCCESS) + { + return res; + } + + // THE PLACE WHERE ACTUAL DATA COPY HAPPENS. + memcpy( + reinterpret_cast(pDstMappedData) + dstAllocRequest.offset, + reinterpret_cast(pSrcMappedData) + srcOffset, + size); + + pDstBlockInfo->m_pBlock->Alloc(dstAllocRequest, suballocType, size); + pSrcBlockInfo->m_pBlock->Free(allocInfo.m_hAllocation); + + allocInfo.m_hAllocation->ChangeBlockAllocation(pDstBlockInfo->m_pBlock, dstAllocRequest.offset); + + if(allocInfo.m_pChanged != VMA_NULL) + { + *allocInfo.m_pChanged = VK_TRUE; + } + + ++m_AllocationsMoved; + m_BytesMoved += size; + + VectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex); + + break; + } + } + + // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round. + + if(srcAllocIndex > 0) + { + --srcAllocIndex; + } + else + { + if(srcBlockIndex > 0) + { + --srcBlockIndex; + srcAllocIndex = SIZE_MAX; + } + else + { + return VK_SUCCESS; + } + } + } +} + +VkResult VmaDefragmentator::Defragment( + VmaBlockVector* pBlockVector, + VkDeviceSize maxBytesToMove, + uint32_t maxAllocationsToMove) +{ + if(m_Allocations.empty()) + { + return VK_SUCCESS; + } + + // Create block info for each block. + const size_t blockCount = pBlockVector->m_Blocks.size(); + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = vma_new(m_pAllocationCallbacks, BlockInfo)(m_pAllocationCallbacks); + pBlockInfo->m_pBlock = pBlockVector->m_Blocks[blockIndex]; + m_Blocks.push_back(pBlockInfo); + } + + // Sort them by m_pBlock pointer value. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess()); + + // Move allocation infos from m_Allocations to appropriate m_Blocks[i].m_Allocations. + for(size_t allocIndex = 0, allocCount = m_Allocations.size(); allocIndex < allocCount; ++allocIndex) + { + AllocationInfo& allocInfo = m_Allocations[allocIndex]; + VmaBlock* pBlock = allocInfo.m_hAllocation->GetBlock(); + BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess()); + if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock) + { + (*it)->m_Allocations.push_back(allocInfo); + } + else + { + VMA_ASSERT(0); + } + } + m_Allocations.clear(); + + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + BlockInfo* pBlockInfo = m_Blocks[blockIndex]; + pBlockInfo->CalcHasNonMovableAllocations(); + pBlockInfo->SortAllocationsBySizeDescecnding(); + } + + // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks. + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination()); + + // Execute defragmentation round (the main part). + VkResult result = VK_SUCCESS; + for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round) + { + result = DefragmentRound(maxBytesToMove, maxAllocationsToMove); + } + + // Unmap blocks that were mapped for defragmentation. + for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + m_Blocks[blockIndex]->Unmap(m_hDevice); + } + + return result; +} + +bool VmaDefragmentator::MoveMakesSense( + size_t dstBlockIndex, VkDeviceSize dstOffset, + size_t srcBlockIndex, VkDeviceSize srcOffset) +{ + if(dstBlockIndex < srcBlockIndex) + { + return true; + } + if(dstBlockIndex > srcBlockIndex) + { + return false; + } + if(dstOffset < srcOffset) + { + return true; + } + return false; +} + //////////////////////////////////////////////////////////////////////////////// // VmaAllocator_T @@ -2867,18 +3797,24 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), m_PreferredLargeHeapBlockSize(0), m_PreferredSmallHeapBlockSize(0), - m_BufferToMemoryMap(VmaStlAllocator< VmaPair >(pCreateInfo->pAllocationCallbacks)), - m_ImageToMemoryMap(VmaStlAllocator< VmaPair >(pCreateInfo->pAllocationCallbacks)) + m_UnmapPersistentlyMappedMemoryCounter(0) { VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device); + memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); memset(&m_MemProps, 0, sizeof(m_MemProps)); memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); - memset(&m_pAllocations, 0, sizeof(m_pAllocations)); - memset(&m_HasEmptyAllocation, 0, sizeof(m_HasEmptyAllocation)); + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_HasEmptyBlock, 0, sizeof(m_HasEmptyBlock)); memset(&m_pOwnAllocations, 0, sizeof(m_pOwnAllocations)); + if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) + { + m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; + m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; + } + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? pCreateInfo->preferredLargeHeapBlockSize : VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ? @@ -2889,42 +3825,32 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : for(size_t i = 0; i < GetMemoryTypeCount(); ++i) { - m_pAllocations[i] = vma_new(this, VmaAllocationVector)(this); - m_pOwnAllocations[i] = vma_new(this, OwnAllocationVectorType)(VmaStlAllocator(GetAllocationCallbacks())); + for(size_t j = 0; j < VMA_BLOCK_VECTOR_TYPE_COUNT; ++j) + { + m_pBlockVectors[i][j] = vma_new(this, VmaBlockVector)(this); + m_pOwnAllocations[i][j] = vma_new(this, AllocationVectorType)(VmaStlAllocator(GetAllocationCallbacks())); + } } } VmaAllocator_T::~VmaAllocator_T() { - for(VMA_MAP_TYPE(VkImage, VkMappedMemoryRange)::iterator it = m_ImageToMemoryMap.begin(); - it != m_ImageToMemoryMap.end(); - ++it) - { - vkDestroyImage(m_hDevice, it->first, GetAllocationCallbacks()); - } - - for(VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange)::iterator it = m_BufferToMemoryMap.begin(); - it != m_BufferToMemoryMap.end(); - ++it) - { - vkDestroyBuffer(m_hDevice, it->first, GetAllocationCallbacks()); - } - for(uint32_t typeIndex = 0; typeIndex < GetMemoryTypeCount(); ++typeIndex) { - OwnAllocationVectorType* pOwnAllocations = m_pOwnAllocations[typeIndex]; - VMA_ASSERT(pOwnAllocations); - for(size_t allocationIndex = 0; allocationIndex < pOwnAllocations->size(); ++allocationIndex) + for(size_t blockVectorType = VMA_BLOCK_VECTOR_TYPE_COUNT; blockVectorType--; ) { - const VmaOwnAllocation& ownAlloc = (*pOwnAllocations)[allocationIndex]; - vkFreeMemory(m_hDevice, ownAlloc.m_hMemory, GetAllocationCallbacks()); + AllocationVectorType* pOwnAllocations = m_pOwnAllocations[typeIndex][blockVectorType]; + VMA_ASSERT(pOwnAllocations != VMA_NULL && pOwnAllocations->size() == 0); } } for(size_t i = GetMemoryTypeCount(); i--; ) { - vma_delete(this, m_pAllocations[i]); - vma_delete(this, m_pOwnAllocations[i]); + for(size_t j = VMA_BLOCK_VECTOR_TYPE_COUNT; j--; ) + { + vma_delete(this, m_pOwnAllocations[i][j]); + vma_delete(this, m_pBlockVectors[i][j]); + } } } @@ -2940,43 +3866,51 @@ VkResult VmaAllocator_T::AllocateMemoryOfType( const VmaMemoryRequirements& vmaMemReq, uint32_t memTypeIndex, VmaSuballocationType suballocType, - VkMappedMemoryRange* pMemory) + VmaAllocation* pAllocation) { + VMA_ASSERT(pAllocation != VMA_NULL); VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size); - pMemory->sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - pMemory->pNext = VMA_NULL; - pMemory->size = vkMemReq.size; - const VkDeviceSize preferredBlockSize = GetPreferredBlockSize(memTypeIndex); // Heuristics: Allocate own memory if requested size if greater than half of preferred block size. const bool ownMemory = - vmaMemReq.ownMemory || + (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT) != 0 || VMA_DEBUG_ALWAYS_OWN_MEMORY || - ((vmaMemReq.neverAllocate == false) && (vkMemReq.size > preferredBlockSize / 2)); + ((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) == 0 && + vkMemReq.size > preferredBlockSize / 2); if(ownMemory) { - if(vmaMemReq.neverAllocate) + if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) != 0) return VK_ERROR_OUT_OF_DEVICE_MEMORY; else - return AllocateOwnMemory(vkMemReq.size, suballocType, memTypeIndex, pMemory); + { + return AllocateOwnMemory( + vkMemReq.size, + suballocType, + memTypeIndex, + (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0, + vmaMemReq.pUserData, + pAllocation); + } } else { - VmaMutexLock lock(m_AllocationsMutex[memTypeIndex]); - VmaAllocationVector* const allocationVector = m_pAllocations[memTypeIndex]; - VMA_ASSERT(allocationVector); + uint32_t blockVectorType = VmaMemoryRequirementFlagsToBlockVectorType(vmaMemReq.flags); + + VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; + VMA_ASSERT(blockVector); // 1. Search existing allocations. // Forward order - prefer blocks with smallest amount of free space. - for(size_t allocIndex = 0; allocIndex < allocationVector->m_Allocations.size(); ++allocIndex ) + for(size_t allocIndex = 0; allocIndex < blockVector->m_Blocks.size(); ++allocIndex ) { - VmaAllocation* const pAlloc = allocationVector->m_Allocations[allocIndex]; - VMA_ASSERT(pAlloc); + VmaBlock* const pBlock = blockVector->m_Blocks[allocIndex]; + VMA_ASSERT(pBlock); VmaAllocationRequest allocRequest = {}; - // Check if can allocate from pAlloc. - if(pAlloc->CreateAllocationRequest( + // Check if can allocate from pBlock. + if(pBlock->CreateAllocationRequest( GetBufferImageGranularity(), vkMemReq.size, vkMemReq.alignment, @@ -2984,23 +3918,28 @@ VkResult VmaAllocator_T::AllocateMemoryOfType( &allocRequest)) { // We no longer have an empty Allocation. - if(pAlloc->IsEmpty()) - m_HasEmptyAllocation[memTypeIndex] = false; - // Allocate from this pAlloc. - pAlloc->Alloc(allocRequest, suballocType, vkMemReq.size); - // Return VkDeviceMemory and offset (size already filled above). - pMemory->memory = pAlloc->m_hMemory; - pMemory->offset = allocRequest.offset; - VMA_HEAVY_ASSERT(pAlloc->Validate()); + if(pBlock->IsEmpty()) + m_HasEmptyBlock[memTypeIndex] = false; + // Allocate from this pBlock. + pBlock->Alloc(allocRequest, suballocType, vkMemReq.size); + *pAllocation = vma_new(this, VmaAllocation_T)(); + (*pAllocation)->InitBlockAllocation( + pBlock, + allocRequest.offset, + vkMemReq.alignment, + vkMemReq.size, + suballocType, + vmaMemReq.pUserData); + VMA_HEAVY_ASSERT(pBlock->Validate()); VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)allocIndex); return VK_SUCCESS; } } // 2. Create new Allocation. - if(vmaMemReq.neverAllocate) + if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) != 0) { - VMA_DEBUG_LOG(" FAILED due to VmaMemoryRequirements::neverAllocate"); + VMA_DEBUG_LOG(" FAILED due to VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT"); return VK_ERROR_OUT_OF_DEVICE_MEMORY; } else @@ -3032,7 +3971,13 @@ VkResult VmaAllocator_T::AllocateMemoryOfType( if(res < 0) { // 5. Try OwnAlloc. - res = AllocateOwnMemory(vkMemReq.size, suballocType, memTypeIndex, pMemory); + res = AllocateOwnMemory( + vkMemReq.size, + suballocType, + memTypeIndex, + (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0, + vmaMemReq.pUserData, + pAllocation); if(res == VK_SUCCESS) { // Succeeded: AllocateOwnMemory function already filld pMemory, nothing more to do here. @@ -3047,20 +3992,54 @@ VkResult VmaAllocator_T::AllocateMemoryOfType( } } - // New VkDeviceMemory successfully created. Create new Allocation for it. - VmaAllocation* const pAlloc = vma_new(this, VmaAllocation)(this); - pAlloc->Init(mem, allocInfo.allocationSize); + // New VkDeviceMemory successfully created. - allocationVector->m_Allocations.push_back(pAlloc); + // Map memory if needed. + void* pMappedData = VMA_NULL; + const bool persistentMap = (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0; + if(persistentMap && m_UnmapPersistentlyMappedMemoryCounter == 0) + { + res = vkMapMemory(m_hDevice, mem, 0, VK_WHOLE_SIZE, 0, &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + vkFreeMemory(m_hDevice, mem, GetAllocationCallbacks()); + return res; + } + } - // Allocate from pAlloc. Because it is empty, allocRequest can be trivially filled. + // Callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, memTypeIndex, mem, allocInfo.allocationSize); + } + + // Create new Allocation for it. + VmaBlock* const pBlock = vma_new(this, VmaBlock)(this); + pBlock->Init( + memTypeIndex, + (VMA_BLOCK_VECTOR_TYPE)blockVectorType, + mem, + allocInfo.allocationSize, + persistentMap, + pMappedData); + + blockVector->m_Blocks.push_back(pBlock); + + // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled. VmaAllocationRequest allocRequest = {}; - allocRequest.freeSuballocationItem = pAlloc->m_Suballocations.begin(); + allocRequest.freeSuballocationItem = pBlock->m_Suballocations.begin(); allocRequest.offset = 0; - pAlloc->Alloc(allocRequest, suballocType, vkMemReq.size); - pMemory->memory = mem; - pMemory->offset = allocRequest.offset; - VMA_HEAVY_ASSERT(pAlloc->Validate()); + pBlock->Alloc(allocRequest, suballocType, vkMemReq.size); + *pAllocation = vma_new(this, VmaAllocation_T)(); + (*pAllocation)->InitBlockAllocation( + pBlock, + allocRequest.offset, + vkMemReq.alignment, + vkMemReq.size, + suballocType, + vmaMemReq.pUserData); + VMA_HEAVY_ASSERT(pBlock->Validate()); VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize); return VK_SUCCESS; } @@ -3071,42 +4050,63 @@ VkResult VmaAllocator_T::AllocateOwnMemory( VkDeviceSize size, VmaSuballocationType suballocType, uint32_t memTypeIndex, - VkMappedMemoryRange* pMemory) + bool map, + void* pUserData, + VmaAllocation* pAllocation) { + VMA_ASSERT(pAllocation); + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; allocInfo.memoryTypeIndex = memTypeIndex; allocInfo.allocationSize = size; // Allocate VkDeviceMemory. - VmaOwnAllocation ownAlloc = {}; - ownAlloc.m_Size = size; - ownAlloc.m_Type = suballocType; - VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &ownAlloc.m_hMemory); + VkDeviceMemory hMemory = VK_NULL_HANDLE; + VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &hMemory); if(res < 0) { VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); return res; } - // Register it in m_pOwnAllocations. - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); - OwnAllocationVectorType* ownAllocations = m_pOwnAllocations[memTypeIndex]; - VMA_ASSERT(ownAllocations); - VmaOwnAllocation* const pOwnAllocationsBeg = ownAllocations->data(); - VmaOwnAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + ownAllocations->size(); - const size_t indexToInsert = VmaBinaryFindFirstNotLess( - pOwnAllocationsBeg, - pOwnAllocationsEnd, - ownAlloc, - VmaOwnAllocationMemoryHandleLess()) - pOwnAllocationsBeg; - VectorInsert(*ownAllocations, indexToInsert, ownAlloc); + void* pMappedData = nullptr; + if(map) + { + if(m_UnmapPersistentlyMappedMemoryCounter == 0) + { + res = vkMapMemory(m_hDevice, hMemory, 0, VK_WHOLE_SIZE, 0, &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + vkFreeMemory(m_hDevice, hMemory, GetAllocationCallbacks()); + return res; + } + } + } - // Return parameters of the allocation. - pMemory->sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; - pMemory->pNext = VMA_NULL; - pMemory->memory = ownAlloc.m_hMemory; - pMemory->offset = 0; - pMemory->size = size; + // Callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, memTypeIndex, hMemory, size); + } + + *pAllocation = vma_new(this, VmaAllocation_T)(); + (*pAllocation)->InitOwnAllocation(memTypeIndex, hMemory, suballocType, map, pMappedData, size, pUserData); + + // Register it in m_pOwnAllocations. + { + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + AllocationVectorType* pOwnAllocations = m_pOwnAllocations[memTypeIndex][map ? VMA_BLOCK_VECTOR_TYPE_MAPPED : VMA_BLOCK_VECTOR_TYPE_UNMAPPED]; + VMA_ASSERT(pOwnAllocations); + VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data(); + VmaAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size(); + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + pOwnAllocationsBeg, + pOwnAllocationsEnd, + *pAllocation, + VmaPointerLess()) - pOwnAllocationsBeg; + VectorInsert(*pOwnAllocations, indexToInsert, *pAllocation); + } VMA_DEBUG_LOG(" Allocated OwnMemory MemoryTypeIndex=#%u", memTypeIndex); @@ -3117,12 +4117,12 @@ VkResult VmaAllocator_T::AllocateMemory( const VkMemoryRequirements& vkMemReq, const VmaMemoryRequirements& vmaMemReq, VmaSuballocationType suballocType, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation) { - if(vmaMemReq.ownMemory && vmaMemReq.neverAllocate) + if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT) != 0 && + (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) != 0) { - VMA_ASSERT(0 && "Specifying VmaMemoryRequirements::ownMemory && VmaMemoryRequirements::neverAllocate makes no sense."); + VMA_ASSERT(0 && "Specifying VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT together with VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT makes no sense."); return VK_ERROR_OUT_OF_DEVICE_MEMORY; } @@ -3132,12 +4132,10 @@ VkResult VmaAllocator_T::AllocateMemory( VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex); if(res == VK_SUCCESS) { - res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pMemory); + res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pAllocation); // Succeeded on first try. if(res == VK_SUCCESS) { - if(pMemoryTypeIndex != VMA_NULL) - *pMemoryTypeIndex = memTypeIndex; return res; } // Allocation from this memory type failed. Try other compatible memory types. @@ -3151,12 +4149,10 @@ VkResult VmaAllocator_T::AllocateMemory( res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex); if(res == VK_SUCCESS) { - res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pMemory); + res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pAllocation); // Allocation from this alternative memory type succeeded. if(res == VK_SUCCESS) { - if(pMemoryTypeIndex != VMA_NULL) - *pMemoryTypeIndex = memTypeIndex; return res; } // else: Allocation from this memory type failed. Try next one - next loop iteration. @@ -3173,63 +4169,58 @@ VkResult VmaAllocator_T::AllocateMemory( return res; } -void VmaAllocator_T::FreeMemory(const VkMappedMemoryRange* pMemory) +void VmaAllocator_T::FreeMemory(const VmaAllocation allocation) { - uint32_t memTypeIndex = 0; - bool found = false; - VmaAllocation* allocationToDelete = VMA_NULL; - // Check all memory types because we don't know which one does pMemory come from. - for(; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + VMA_ASSERT(allocation); + + if(allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) { - VmaMutexLock lock(m_AllocationsMutex[memTypeIndex]); - VmaAllocationVector* const pAllocationVector = m_pAllocations[memTypeIndex]; - VMA_ASSERT(pAllocationVector); - // Try to free pMemory from pAllocationVector. - const size_t allocIndex = pAllocationVector->Free(pMemory); - if(allocIndex != (size_t)-1) + VmaBlock* pBlockToDelete = VMA_NULL; + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + const VMA_BLOCK_VECTOR_TYPE blockVectorType = allocation->GetBlockVectorType(); { + VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; + VmaBlock* pBlock = allocation->GetBlock(); + + pBlock->Free(allocation); + VMA_HEAVY_ASSERT(pBlock->Validate()); + VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex); - found = true; - VmaAllocation* const pAlloc = pAllocationVector->m_Allocations[allocIndex]; - VMA_ASSERT(pAlloc); - // pAlloc became empty after this deallocation. - if(pAlloc->IsEmpty()) + + // pBlock became empty after this deallocation. + if(pBlock->IsEmpty()) { // Already has empty Allocation. We don't want to have two, so delete this one. - if(m_HasEmptyAllocation[memTypeIndex]) + if(m_HasEmptyBlock[memTypeIndex]) { - allocationToDelete = pAlloc; - VectorRemove(pAllocationVector->m_Allocations, allocIndex); - break; + pBlockToDelete = pBlock; + pBlockVector->Remove(pBlock); } // We now have first empty Allocation. else - m_HasEmptyAllocation[memTypeIndex] = true; + m_HasEmptyBlock[memTypeIndex] = true; } - // Must be called after allocIndex is used, because later it may become invalid! - pAllocationVector->IncrementallySortAllocations(); - break; + // Must be called after srcBlockIndex is used, because later it may become invalid! + pBlockVector->IncrementallySortBlocks(); } - } - if(found) - { // Destruction of a free Allocation. Deferred until this point, outside of mutex // lock, for performance reason. - if(allocationToDelete != VMA_NULL) + if(pBlockToDelete != VMA_NULL) { VMA_DEBUG_LOG(" Deleted empty allocation"); - allocationToDelete->Destroy(this); - vma_delete(this, allocationToDelete); + pBlockToDelete->Destroy(this); + vma_delete(this, pBlockToDelete); } - return; + + vma_delete(this, allocation); + } + else // VmaAllocation_T::ALLOCATION_TYPE_OWN + { + FreeOwnMemory(allocation); } - - // pMemory not found in allocations. Try free it as Own Memory. - if(FreeOwnMemory(pMemory)) - return; - - // pMemory not found as Own Memory either. - VMA_ASSERT(0 && "Not found. Trying to free memory not allocated using this allocator (or some other bug)."); } void VmaAllocator_T::CalculateStats(VmaStats* pStats) @@ -3242,11 +4233,14 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats) for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock allocationsLock(m_AllocationsMutex[memTypeIndex]); + VmaMutexLock allocationsLock(m_BlocksMutex[memTypeIndex]); const uint32_t heapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex; - const VmaAllocationVector* const allocVector = m_pAllocations[memTypeIndex]; - VMA_ASSERT(allocVector); - allocVector->AddStats(pStats, memTypeIndex, heapIndex); + for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) + { + const VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; + VMA_ASSERT(pBlockVector); + pBlockVector->AddStats(pStats, memTypeIndex, heapIndex); + } } VmaPostprocessCalcStatInfo(pStats->total); @@ -3256,44 +4250,286 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats) VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]); } -bool VmaAllocator_T::FreeOwnMemory(const VkMappedMemoryRange* pMemory) -{ - VkDeviceMemory vkMemory = VK_NULL_HANDLE; +static const uint32_t VMA_VENDOR_ID_AMD = 4098; - // Check all memory types because we don't know which one does pMemory come from. +void VmaAllocator_T::UnmapPersistentlyMappedMemory() +{ + if(m_UnmapPersistentlyMappedMemoryCounter++ == 0) + { + if(m_PhysicalDeviceProperties.vendorID == VMA_VENDOR_ID_AMD) + { + for(size_t memTypeIndex = m_MemProps.memoryTypeCount; memTypeIndex--; ) + { + const VkMemoryPropertyFlags memFlags = m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0 && + (memFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) + { + // Process OwnAllocations. + { + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + AllocationVectorType* pOwnAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; + for(size_t ownAllocIndex = pOwnAllocationsVector->size(); ownAllocIndex--; ) + { + VmaAllocation hAlloc = (*pOwnAllocationsVector)[ownAllocIndex]; + hAlloc->OwnAllocUnmapPersistentlyMappedMemory(m_hDevice); + } + } + + // Process normal Allocations. + { + VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; + pBlockVector->UnmapPersistentlyMappedMemory(); + } + } + } + } + } +} + +VkResult VmaAllocator_T::MapPersistentlyMappedMemory() +{ + VMA_ASSERT(m_UnmapPersistentlyMappedMemoryCounter > 0); + if(--m_UnmapPersistentlyMappedMemoryCounter == 0) + { + VkResult finalResult = VK_SUCCESS; + if(m_PhysicalDeviceProperties.vendorID == VMA_VENDOR_ID_AMD) + { + for(size_t memTypeIndex = 0; memTypeIndex < m_MemProps.memoryTypeCount; ++memTypeIndex) + { + const VkMemoryPropertyFlags memFlags = m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0 && + (memFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0) + { + // Process OwnAllocations. + { + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + AllocationVectorType* pAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; + for(size_t ownAllocIndex = 0, ownAllocCount = pAllocationsVector->size(); ownAllocIndex < ownAllocCount; ++ownAllocIndex) + { + VmaAllocation hAlloc = (*pAllocationsVector)[ownAllocIndex]; + hAlloc->OwnAllocMapPersistentlyMappedMemory(m_hDevice); + } + } + + // Process normal Allocations. + { + VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; + VkResult localResult = pBlockVector->MapPersistentlyMappedMemory(); + if(localResult != VK_SUCCESS) + { + finalResult = localResult; + } + } + } + } + } + return finalResult; + } + else + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::Defragment( + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo* pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats) +{ + if(pAllocationsChanged != VMA_NULL) + { + memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged)); + } + if(pDefragmentationStats != VMA_NULL) + { + memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats)); + } + + if(m_UnmapPersistentlyMappedMemoryCounter > 0) + { + VMA_DEBUG_LOG("ERROR: Cannot defragment when inside vmaUnmapPersistentlyMappedMemory."); + return VK_ERROR_MEMORY_MAP_FAILED; + } + + // Initialize defragmentators per memory type. + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + VmaDefragmentator* pDefragmentators[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT]; + memset(pDefragmentators, 0, sizeof(pDefragmentators)); for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); - OwnAllocationVectorType* const pOwnAllocations = m_pOwnAllocations[memTypeIndex]; - VMA_ASSERT(pOwnAllocations); - VmaOwnAllocation* const pOwnAllocationsBeg = pOwnAllocations->data(); - VmaOwnAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size(); - VmaOwnAllocation* const pOwnAllocationIt = VmaBinaryFindFirstNotLess( - pOwnAllocationsBeg, - pOwnAllocationsEnd, - pMemory->memory, - VmaOwnAllocationMemoryHandleLess()); - if((pOwnAllocationIt != pOwnAllocationsEnd) && - (pOwnAllocationIt->m_hMemory == pMemory->memory)) + // Only HOST_VISIBLE memory types can be defragmented. + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) { - VMA_ASSERT(pMemory->size == pOwnAllocationIt->m_Size && pMemory->offset == 0); - vkMemory = pOwnAllocationIt->m_hMemory; - const size_t ownAllocationIndex = pOwnAllocationIt - pOwnAllocationsBeg; - VectorRemove(*pOwnAllocations, ownAllocationIndex); - VMA_DEBUG_LOG(" Freed OwnMemory MemoryTypeIndex=%u", memTypeIndex); - break; + for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) + { + pDefragmentators[memTypeIndex][blockVectorType] = vma_new(this, VmaDefragmentator)( + m_hDevice, + GetAllocationCallbacks(), + bufferImageGranularity, + memTypeIndex, + (VMA_BLOCK_VECTOR_TYPE)blockVectorType); + } } } - // Found. Free VkDeviceMemory deferred until this point, outside of mutex lock, - // for performance reason. - if(vkMemory != VK_NULL_HANDLE) + // Dispatch pAllocations among defragmentators. + for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) { - vkFreeMemory(m_hDevice, vkMemory, GetAllocationCallbacks()); - return true; + VmaAllocation hAlloc = pAllocations[allocIndex]; + VMA_ASSERT(hAlloc); + if(hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) + { + const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); + // Only HOST_VISIBLE memory types can be defragmented. + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + const VMA_BLOCK_VECTOR_TYPE blockVectorType = hAlloc->GetBlockVectorType(); + VkBool32* pChanged = (pAllocationsChanged != VMA_NULL) ? + &pAllocationsChanged[allocIndex] : VMA_NULL; + pDefragmentators[memTypeIndex][blockVectorType]->AddAllocation(hAlloc, pChanged); + } + // else: skip this allocation, cannot move it. + } + // else ALLOCATION_TYPE_OWN: skip this allocation, nothing to defragment. } - else - return false; + + VkResult result = VK_SUCCESS; + + // Main processing. + VkDeviceSize maxBytesToMove = SIZE_MAX; + uint32_t maxAllocationsToMove = UINT_MAX; + if(pDefragmentationInfo != VMA_NULL) + { + maxBytesToMove = pDefragmentationInfo->maxBytesToMove; + maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove; + } + for(uint32_t memTypeIndex = 0; + (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS); + ++memTypeIndex) + { + // Only HOST_VISIBLE memory types can be defragmented. + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + + for(uint32_t blockVectorType = 0; + (blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT) && (result == VK_SUCCESS); + ++blockVectorType) + { + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; + + // Defragment. + result = pDefragmentators[memTypeIndex][blockVectorType]->Defragment(pBlockVector, maxBytesToMove, maxAllocationsToMove); + + // Accumulate statistics. + if(pDefragmentationStats != VMA_NULL) + { + const VkDeviceSize bytesMoved = pDefragmentators[memTypeIndex][blockVectorType]->GetBytesMoved(); + const uint32_t allocationsMoved = pDefragmentators[memTypeIndex][blockVectorType]->GetAllocationsMoved(); + pDefragmentationStats->bytesMoved += bytesMoved; + pDefragmentationStats->allocationsMoved += allocationsMoved; + VMA_ASSERT(bytesMoved <= maxBytesToMove); + VMA_ASSERT(allocationsMoved <= maxAllocationsToMove); + maxBytesToMove -= bytesMoved; + maxAllocationsToMove -= allocationsMoved; + } + + // Free empty blocks. + for(size_t blockIndex = pBlockVector->m_Blocks.size(); blockIndex--; ) + { + VmaBlock* pBlock = pBlockVector->m_Blocks[blockIndex]; + if(pBlock->IsEmpty()) + { + if(pDefragmentationStats != VMA_NULL) + { + ++pDefragmentationStats->deviceMemoryBlocksFreed; + pDefragmentationStats->bytesFreed += pBlock->m_Size; + } + + VectorRemove(pBlockVector->m_Blocks, blockIndex); + pBlock->Destroy(this); + vma_delete(this, pBlock); + } + } + + // All block vector types processed: we can be sure that all empty allocations have been freed. + if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_COUNT - 1) + { + m_HasEmptyBlock[memTypeIndex] = false; + } + } + } + } + + // Destroy defragmentators. + for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) + { + for(size_t blockVectorType = VMA_BLOCK_VECTOR_TYPE_COUNT; blockVectorType--; ) + { + vma_delete(this, pDefragmentators[memTypeIndex][blockVectorType]); + } + } + + return result; +} + +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(); +} + +void VmaAllocator_T::FreeOwnMemory(VmaAllocation allocation) +{ + VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_OWN); + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + { + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + AllocationVectorType* const pOwnAllocations = m_pOwnAllocations[memTypeIndex][allocation->GetBlockVectorType()]; + VMA_ASSERT(pOwnAllocations); + VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data(); + VmaAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size(); + VmaAllocation* const pOwnAllocationIt = VmaBinaryFindFirstNotLess( + pOwnAllocationsBeg, + pOwnAllocationsEnd, + allocation, + VmaPointerLess()); + if(pOwnAllocationIt != pOwnAllocationsEnd) + { + const size_t ownAllocationIndex = pOwnAllocationIt - pOwnAllocationsBeg; + VectorRemove(*pOwnAllocations, ownAllocationIndex); + } + else + { + VMA_ASSERT(0); + } + } + + VkDeviceMemory hMemory = allocation->GetMemory(); + + // Callback. + if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnFree)(this, memTypeIndex, hMemory, allocation->GetSize()); + } + + if(allocation->GetMappedData() != VMA_NULL) + { + vkUnmapMemory(m_hDevice, hMemory); + } + + vkFreeMemory(m_hDevice, hMemory, GetAllocationCallbacks()); + + VMA_DEBUG_LOG(" Freed OwnMemory MemoryTypeIndex=%u", memTypeIndex); + + vma_delete(this, allocation); } #if VMA_STATS_STRING_ENABLED @@ -3304,34 +4540,41 @@ void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb) for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { VmaMutexLock ownAllocationsLock(m_OwnAllocationsMutex[memTypeIndex]); - OwnAllocationVectorType* const pOwnAllocVector = m_pOwnAllocations[memTypeIndex]; - VMA_ASSERT(pOwnAllocVector); - if(pOwnAllocVector->empty() == false) + for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) { - if(ownAllocationsStarted) - sb.Add(",\n\t\"Type "); - else + AllocationVectorType* const pOwnAllocVector = m_pOwnAllocations[memTypeIndex][blockVectorType]; + VMA_ASSERT(pOwnAllocVector); + if(pOwnAllocVector->empty() == false) { - sb.Add(",\n\"OwnAllocations\": {\n\t\"Type "); - ownAllocationsStarted = true; - } - sb.AddNumber(memTypeIndex); - sb.Add("\": ["); - - for(size_t i = 0; i < pOwnAllocVector->size(); ++i) - { - const VmaOwnAllocation& ownAlloc = (*pOwnAllocVector)[i]; - if(i > 0) - sb.Add(",\n\t\t{ \"Size\": "); + if(ownAllocationsStarted) + sb.Add(",\n\t\"Type "); else - sb.Add("\n\t\t{ \"Size\": "); - sb.AddNumber(ownAlloc.m_Size); - sb.Add(", \"Type\": "); - sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[ownAlloc.m_Type]); - sb.Add(" }"); - } + { + sb.Add(",\n\"OwnAllocations\": {\n\t\"Type "); + ownAllocationsStarted = true; + } + sb.AddNumber(memTypeIndex); + if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED) + { + sb.Add(" Mapped"); + } + sb.Add("\": ["); - sb.Add("\n\t]"); + for(size_t i = 0; i < pOwnAllocVector->size(); ++i) + { + const VmaAllocation hAlloc = (*pOwnAllocVector)[i]; + if(i > 0) + sb.Add(",\n\t\t{ \"Size\": "); + else + sb.Add("\n\t\t{ \"Size\": "); + sb.AddNumber(hAlloc->GetSize()); + sb.Add(", \"Type\": "); + sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]); + sb.Add(" }"); + } + + sb.Add("\n\t]"); + } } } if(ownAllocationsStarted) @@ -3341,22 +4584,29 @@ void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb) bool allocationsStarted = false; for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock globalAllocationsLock(m_AllocationsMutex[memTypeIndex]); - if(m_pAllocations[memTypeIndex]->IsEmpty() == false) + VmaMutexLock globalAllocationsLock(m_BlocksMutex[memTypeIndex]); + for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) { - if(allocationsStarted) - sb.Add(",\n\t\"Type "); - else + if(m_pBlockVectors[memTypeIndex][blockVectorType]->IsEmpty() == false) { - sb.Add(",\n\"Allocations\": {\n\t\"Type "); - allocationsStarted = true; + if(allocationsStarted) + sb.Add(",\n\t\"Type "); + else + { + sb.Add(",\n\"Allocations\": {\n\t\"Type "); + allocationsStarted = true; + } + sb.AddNumber(memTypeIndex); + if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED) + { + sb.Add(" Mapped"); + } + sb.Add("\": ["); + + m_pBlockVectors[memTypeIndex][blockVectorType]->PrintDetailedMap(sb); + + sb.Add("\n\t]"); } - sb.AddNumber(memTypeIndex); - sb.Add("\": ["); - - m_pAllocations[memTypeIndex]->PrintDetailedMap(sb); - - sb.Add("\n\t]"); } } if(allocationsStarted) @@ -3371,10 +4621,9 @@ static VkResult AllocateMemoryForImage( VkImage image, const VmaMemoryRequirements* pMemoryRequirements, VmaSuballocationType suballocType, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation) { - VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements && pMemory); + VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pMemoryRequirements && pAllocation); VkMemoryRequirements vkMemReq = {}; vkGetImageMemoryRequirements(allocator->m_hDevice, image, &vkMemReq); @@ -3383,8 +4632,7 @@ static VkResult AllocateMemoryForImage( vkMemReq, *pMemoryRequirements, suballocType, - pMemory, - pMemoryTypeIndex); + pAllocation); } //////////////////////////////////////////////////////////////////////////////// @@ -3579,6 +4827,9 @@ VkResult vmaFindMemoryTypeIndex( break; } + if((pMemoryRequirements->flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0) + requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + *pMemoryTypeIndex = UINT_MAX; uint32_t minCost = UINT_MAX; for(uint32_t memTypeIndex = 0, memTypeBit = 1; @@ -3613,10 +4864,10 @@ VkResult vmaAllocateMemory( VmaAllocator allocator, const VkMemoryRequirements* pVkMemoryRequirements, const VmaMemoryRequirements* pVmaMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) { - VMA_ASSERT(allocator && pVkMemoryRequirements && pVmaMemoryRequirements && pMemory); + VMA_ASSERT(allocator && pVkMemoryRequirements && pVmaMemoryRequirements && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemory"); @@ -3626,18 +4877,22 @@ VkResult vmaAllocateMemory( *pVkMemoryRequirements, *pVmaMemoryRequirements, VMA_SUBALLOCATION_TYPE_UNKNOWN, - pMemory, - pMemoryTypeIndex); + pAllocation); + + if(pAllocationInfo) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } } VkResult vmaAllocateMemoryForBuffer( VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements* pMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) { - VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pMemoryRequirements && pMemory); + VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pMemoryRequirements && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); @@ -3650,18 +4905,22 @@ VkResult vmaAllocateMemoryForBuffer( vkMemReq, *pMemoryRequirements, VMA_SUBALLOCATION_TYPE_BUFFER, - pMemory, - pMemoryTypeIndex); + pAllocation); + + if(pAllocationInfo) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } } VkResult vmaAllocateMemoryForImage( VmaAllocator allocator, VkImage image, const VmaMemoryRequirements* pMemoryRequirements, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) { - VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements); + VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements && pAllocation); VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); @@ -3672,45 +4931,108 @@ VkResult vmaAllocateMemoryForImage( image, pMemoryRequirements, VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, - pMemory, - pMemoryTypeIndex); + pAllocation); + + if(pAllocationInfo) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } } void vmaFreeMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory) + VmaAllocation allocation) { - VMA_ASSERT(allocator && pMemory); + VMA_ASSERT(allocator && allocation); VMA_DEBUG_LOG("vmaFreeMemory"); VMA_DEBUG_GLOBAL_MUTEX_LOCK - allocator->FreeMemory(pMemory); + allocator->FreeMemory(allocation); +} + +void vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetAllocationInfo(allocation, pAllocationInfo); +} + +void vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocation->SetUserData(pUserData); } VkResult vmaMapMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory, + VmaAllocation allocation, void** ppData) { - VMA_ASSERT(allocator && pMemory && ppData); + VMA_ASSERT(allocator && allocation && ppData); VMA_DEBUG_GLOBAL_MUTEX_LOCK - return vkMapMemory(allocator->m_hDevice, pMemory->memory, - pMemory->offset, pMemory->size, 0, ppData); + return vkMapMemory(allocator->m_hDevice, allocation->GetMemory(), + allocation->GetOffset(), allocation->GetSize(), 0, ppData); } void vmaUnmapMemory( VmaAllocator allocator, - const VkMappedMemoryRange* pMemory) + VmaAllocation allocation) { - VMA_ASSERT(allocator && pMemory); + VMA_ASSERT(allocator && allocation); VMA_DEBUG_GLOBAL_MUTEX_LOCK - vkUnmapMemory(allocator->m_hDevice, pMemory->memory); + vkUnmapMemory(allocator->m_hDevice, allocation->GetMemory()); +} + +void vmaUnmapPersistentlyMappedMemory(VmaAllocator allocator) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->UnmapPersistentlyMappedMemory(); +} + +VkResult vmaMapPersistentlyMappedMemory(VmaAllocator allocator) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->MapPersistentlyMappedMemory(); +} + +VkResult vmaDefragment( + VmaAllocator allocator, + VmaAllocation* pAllocations, + size_t allocationCount, + VkBool32* pAllocationsChanged, + const VmaDefragmentationInfo *pDefragmentationInfo, + VmaDefragmentationStats* pDefragmentationStats) +{ + VMA_ASSERT(allocator && pAllocations); + + VMA_DEBUG_LOG("vmaDefragment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats); } VkResult vmaCreateBuffer( @@ -3718,10 +5040,10 @@ VkResult vmaCreateBuffer( const VkBufferCreateInfo* pCreateInfo, const VmaMemoryRequirements* pMemoryRequirements, VkBuffer* pBuffer, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) { - VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements); + VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements && pBuffer && pAllocation); VMA_DEBUG_LOG("vmaCreateBuffer"); @@ -3731,8 +5053,6 @@ VkResult vmaCreateBuffer( VkResult res = vkCreateBuffer(allocator->m_hDevice, pCreateInfo, allocator->GetAllocationCallbacks(), pBuffer); if(res >= 0) { - VkMappedMemoryRange mem = {}; - // 2. vkGetBufferMemoryRequirements. VkMemoryRequirements vkMemReq = {}; vkGetBufferMemoryRequirements(allocator->m_hDevice, *pBuffer, &vkMemReq); @@ -3742,24 +5062,21 @@ VkResult vmaCreateBuffer( vkMemReq, *pMemoryRequirements, VMA_SUBALLOCATION_TYPE_BUFFER, - &mem, - pMemoryTypeIndex); + pAllocation); if(res >= 0) { - if(pMemory != VMA_NULL) - { - *pMemory = mem; - } // 3. Bind buffer with memory. - res = vkBindBufferMemory(allocator->m_hDevice, *pBuffer, mem.memory, mem.offset); + res = vkBindBufferMemory(allocator->m_hDevice, *pBuffer, (*pAllocation)->GetMemory(), (*pAllocation)->GetOffset()); if(res >= 0) { // All steps succeeded. - VmaMutexLock lock(allocator->m_BufferToMemoryMapMutex); - allocator->m_BufferToMemoryMap.insert(VmaPair(*pBuffer, mem)); + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } return VK_SUCCESS; } - allocator->FreeMemory(&mem); + allocator->FreeMemory(*pAllocation); return res; } vkDestroyBuffer(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); @@ -3770,7 +5087,8 @@ VkResult vmaCreateBuffer( void vmaDestroyBuffer( VmaAllocator allocator, - VkBuffer buffer) + VkBuffer buffer, + VmaAllocation allocation) { if(buffer != VK_NULL_HANDLE) { @@ -3780,22 +5098,9 @@ void vmaDestroyBuffer( VMA_DEBUG_GLOBAL_MUTEX_LOCK - VkMappedMemoryRange mem = {}; - { - VmaMutexLock lock(allocator->m_BufferToMemoryMapMutex); - VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange)::iterator it = allocator->m_BufferToMemoryMap.find(buffer); - if(it == allocator->m_BufferToMemoryMap.end()) - { - VMA_ASSERT(0 && "Trying to destroy buffer that was not created using vmaCreateBuffer or already freed."); - return; - } - mem = it->second; - allocator->m_BufferToMemoryMap.erase(it); - } - vkDestroyBuffer(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); - allocator->FreeMemory(&mem); + allocator->FreeMemory(allocation); } } @@ -3804,10 +5109,10 @@ VkResult vmaCreateImage( const VkImageCreateInfo* pCreateInfo, const VmaMemoryRequirements* pMemoryRequirements, VkImage* pImage, - VkMappedMemoryRange* pMemory, - uint32_t* pMemoryTypeIndex) + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) { - VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements); + VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements && pImage && pAllocation); VMA_DEBUG_LOG("vmaCreateImage"); @@ -3823,21 +5128,21 @@ VkResult vmaCreateImage( VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; // 2. Allocate memory using allocator. - res = AllocateMemoryForImage(allocator, *pImage, pMemoryRequirements, suballocType, &mem, pMemoryTypeIndex); + res = AllocateMemoryForImage(allocator, *pImage, pMemoryRequirements, suballocType, pAllocation); if(res >= 0) { - if(pMemory != VMA_NULL) - *pMemory = mem; // 3. Bind image with memory. - res = vkBindImageMemory(allocator->m_hDevice, *pImage, mem.memory, mem.offset); + res = vkBindImageMemory(allocator->m_hDevice, *pImage, (*pAllocation)->GetMemory(), (*pAllocation)->GetOffset()); if(res >= 0) { // All steps succeeded. - VmaMutexLock lock(allocator->m_ImageToMemoryMapMutex); - allocator->m_ImageToMemoryMap.insert(VmaPair(*pImage, mem)); + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } return VK_SUCCESS; } - allocator->FreeMemory(&mem); + allocator->FreeMemory(*pAllocation); return res; } vkDestroyImage(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); @@ -3848,7 +5153,8 @@ VkResult vmaCreateImage( void vmaDestroyImage( VmaAllocator allocator, - VkImage image) + VkImage image, + VmaAllocation allocation) { if(image != VK_NULL_HANDLE) { @@ -3858,25 +5164,10 @@ void vmaDestroyImage( VMA_DEBUG_GLOBAL_MUTEX_LOCK - VkMappedMemoryRange mem = {}; - { - VmaMutexLock lock(allocator->m_ImageToMemoryMapMutex); - VMA_MAP_TYPE(VkImage, VkMappedMemoryRange)::iterator it = allocator->m_ImageToMemoryMap.find(image); - if(it == allocator->m_ImageToMemoryMap.end()) - { - VMA_ASSERT(0 && "Trying to destroy buffer that was not created using vmaCreateBuffer or already freed."); - return; - } - mem = it->second; - allocator->m_ImageToMemoryMap.erase(it); - } - vkDestroyImage(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); - allocator->FreeMemory(&mem); + allocator->FreeMemory(allocation); } } #endif // #ifdef VMA_IMPLEMENTATION - -#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H From 7ec64121d8dfb576403d4db97043575cc12ebb9f Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 4 Jul 2017 15:45:59 +0200 Subject: [PATCH 02/11] Fixed for Linux GCC compilation. (Cherry pick from v1) --- src/vk_mem_alloc.h | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 660a338..3d23b0a 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -679,6 +679,7 @@ void vmaDestroyImage( #ifdef VMA_IMPLEMENTATION #undef VMA_IMPLEMENTATION +#include #include /******************************************************************************* @@ -721,6 +722,11 @@ remove them if not needed. #include // for min, max #include // for std::mutex +#if !defined(_WIN32) + #include // for aligned_alloc() +#endif + + #ifdef _DEBUG // Normal assert to check for programmer's errors, especially in Debug configuration. #define VMA_ASSERT(expr) assert(expr) @@ -736,8 +742,14 @@ remove them if not needed. #define VMA_NULL nullptr #define VMA_ALIGN_OF(type) (__alignof(type)) -#define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) -#define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) + +#if defined(_WIN32) + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) + #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) +#else + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) + #define VMA_SYSTEM_FREE(ptr) free(ptr) +#endif #define VMA_MIN(v1, v2) (std::min((v1), (v2))) #define VMA_MAX(v1, v2) (std::max((v1), (v2))) @@ -1109,6 +1121,8 @@ public: { return m_pCallbacks != rhs.m_pCallbacks; } + + VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete; }; #if VMA_USE_STL_VECTOR @@ -1142,6 +1156,13 @@ public: m_Count(0), m_Capacity(0) { + } + + m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) + { } VmaVector(size_t count, const AllocatorT& allocator) : @@ -1154,7 +1175,7 @@ public: VmaVector(const VmaVector& src) : m_Allocator(src.m_Allocator), - m_pArray(src.m_Count ? (T*)VmaAllocateArray(allocator->m_pCallbacks, src.m_Count) : VMA_NULL), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src->m_pCallbacks, src.m_Count) : VMA_NULL), m_Count(src.m_Count), m_Capacity(src.m_Count) { @@ -1224,7 +1245,7 @@ public: if(newCapacity != m_Capacity) { - T* const newArray = newCapacity ? VmaAllocateArray(m_hAllocator, newCapacity) : VMA_NULL; + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; if(m_Count != 0) memcpy(newArray, m_pArray, m_Count * sizeof(T)); VmaFree(m_Allocator.m_pCallbacks, m_pArray); @@ -1397,7 +1418,7 @@ T* VmaPoolAllocator::Alloc() { ItemBlock& block = m_ItemBlocks[i]; // This block has some free items: Use first one. - if(block.FirstFreeIndex != UINT_MAX) + if(block.FirstFreeIndex != UINT32_MAX) { Item* const pItem = &block.pItems[block.FirstFreeIndex]; block.FirstFreeIndex = pItem->NextFreeIndex; @@ -1447,7 +1468,7 @@ typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() // Setup singly-linked list of all free items in this block. for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i) newBlock.pItems[i].NextFreeIndex = i + 1; - newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT_MAX; + newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX; return m_ItemBlocks.back(); } @@ -1608,7 +1629,7 @@ VmaListItem* VmaRawList::PushFront(const T& value) { ItemType* const pNewItem = PushFront(); pNewItem->Value = value; - return newItem; + return pNewItem; } template @@ -2776,7 +2797,7 @@ bool VmaBlock::Validate() const How many suitable free suballocations to analyze before choosing best one. - Set to 1 to use First-Fit algorithm - first suitable free suballocation will be chosen. -- Set to UINT_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free +- Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free suballocations will be analized and best one will be chosen. - Any other value is also acceptable. */ @@ -2798,8 +2819,6 @@ bool VmaBlock::CreateAllocationRequest( if(m_SumFreeSize < allocSize) return false; - bool found = false; - // Old brute-force algorithm, linearly searching suballocations. /* uint32_t suitableSuballocationsFound = 0; @@ -4128,7 +4147,7 @@ VkResult VmaAllocator_T::AllocateMemory( // Bit mask of memory Vulkan types acceptable for this allocation. uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; - uint32_t memTypeIndex = UINT_MAX; + uint32_t memTypeIndex = UINT32_MAX; VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex); if(res == VK_SUCCESS) { @@ -4857,7 +4876,7 @@ VkResult vmaFindMemoryTypeIndex( } } } - return (*pMemoryTypeIndex != UINT_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; + return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; } VkResult vmaAllocateMemory( From b0425876ecfb24d4efd98813ac493293e79f4de0 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 4 Jul 2017 15:47:00 +0200 Subject: [PATCH 03/11] Fix for Linux GCC cont'd. Enabled warning level 4 in Visual Studio for compilation of vk_mem_alloc.h. (Cherry pick from v1) --- src/VulkanSample.cpp | 5 ++++- src/vk_mem_alloc.h | 17 +++++------------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/VulkanSample.cpp b/src/VulkanSample.cpp index 4f2fed7..5a2cfe4 100644 --- a/src/VulkanSample.cpp +++ b/src/VulkanSample.cpp @@ -27,8 +27,11 @@ #define VK_USE_PLATFORM_WIN32_KHR #include +#pragma warning(push, 4) +#pragma warning(disable: 4127) // warning C4127: conditional expression is constant #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" +#pragma warning(pop) #define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT #include @@ -1714,7 +1717,7 @@ int main() RECT rect = { 0, 0, g_SizeX, g_SizeY }; AdjustWindowRectEx(&rect, style, FALSE, exStyle); - HWND hWnd = CreateWindowEx( + CreateWindowEx( exStyle, WINDOW_CLASS_NAME, APP_TITLE_W, style, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, g_hAppInstance, NULL); diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 3d23b0a..e230011 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -528,7 +528,7 @@ typedef struct VmaDefragmentationInfo { VkDeviceSize maxBytesToMove; /** \brief Maximum number of allocations that can be moved to different place. - Default is UINT_MAX, which means no limit. + Default is UINT32_MAX, which means no limit. */ uint32_t maxAllocationsToMove; } VmaDefragmentationInfo; @@ -1158,13 +1158,6 @@ public: { } - m_Allocator(allocator), - m_pArray(VMA_NULL), - m_Count(0), - m_Capacity(0) - { - } - VmaVector(size_t count, const AllocatorT& allocator) : m_Allocator(allocator), m_pArray(count ? (T*)VmaAllocateArray(allocator->m_pCallbacks, count) : VMA_NULL), @@ -2653,7 +2646,7 @@ struct VmaSuballocationItemSizeLess }; VmaBlock::VmaBlock(VmaAllocator hAllocator) : - m_MemoryTypeIndex(UINT_MAX), + m_MemoryTypeIndex(UINT32_MAX), m_BlockVectorType(VMA_BLOCK_VECTOR_TYPE_COUNT), m_hMemory(VK_NULL_HANDLE), m_Size(0), @@ -4418,7 +4411,7 @@ VkResult VmaAllocator_T::Defragment( // Main processing. VkDeviceSize maxBytesToMove = SIZE_MAX; - uint32_t maxAllocationsToMove = UINT_MAX; + uint32_t maxAllocationsToMove = UINT32_MAX; if(pDefragmentationInfo != VMA_NULL) { maxBytesToMove = pDefragmentationInfo->maxBytesToMove; @@ -4849,8 +4842,8 @@ VkResult vmaFindMemoryTypeIndex( if((pMemoryRequirements->flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0) requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; - *pMemoryTypeIndex = UINT_MAX; - uint32_t minCost = UINT_MAX; + *pMemoryTypeIndex = UINT32_MAX; + uint32_t minCost = UINT32_MAX; for(uint32_t memTypeIndex = 0, memTypeBit = 1; memTypeIndex < allocator->GetMemoryTypeCount(); ++memTypeIndex, memTypeBit <<= 1) From 2a22d612975b9e79e22a39e142e90be7c2474725 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 4 Jul 2017 15:52:30 +0200 Subject: [PATCH 04/11] Changed "CONFIGURATION SECTION" to contain #ifndef so you can define these macros before including this header, not necessarily change them in the file. (Cherry pick from v1) --- src/vk_mem_alloc.h | 290 ++++++++++++++++++++++++++------------------- 1 file changed, 170 insertions(+), 120 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index e230011..60d3de1 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -122,15 +122,11 @@ When you want to create a buffer or image: \section configuration Configuration -Set VMA_STATS_STRING_ENABLED macro in vk_mem_alloc.h to 0 or 1 to disable/enable -compilation of code for dumping internal allocator state to string in JSON -format. - -Please check "CONFIGURATION SECTION" below to find macros and other definitions -that you can change to connect the library to your own implementation of basic -facilities like assert, min and max functions, mutex etc. C++ STL is used by -default, but changing these allows you to get rid of any STL usage if you want, -as many game developers tend to do. +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 etc. C++ STL is used by default, but changing these allows you to get rid +of any STL usage if you want, as many game developers tend to do. \section custom_memory_allocator Custom memory allocator @@ -685,10 +681,12 @@ void vmaDestroyImage( /******************************************************************************* CONFIGURATION SECTION -Change these definitions depending on your environment. +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. */ -#define VMA_USE_STL_CONTAINERS 0 +// Define this macro to 1 to make the library use STL containers instead of its own implementation. +//#define VMA_USE_STL_CONTAINERS 1 /* Set this macro to 1 to make the library including and using STL containers: std::pair, std::vector, std::list, std::unordered_map. @@ -697,21 +695,21 @@ Set it to 0 or undefined to make the library using its own implementation of the containers. */ #if VMA_USE_STL_CONTAINERS -#define VMA_USE_STL_VECTOR 1 -#define VMA_USE_STL_UNORDERED_MAP 1 -#define VMA_USE_STL_LIST 1 + #define VMA_USE_STL_VECTOR 1 + #define VMA_USE_STL_UNORDERED_MAP 1 + #define VMA_USE_STL_LIST 1 #endif #if VMA_USE_STL_VECTOR -#include + #include #endif #if VMA_USE_STL_UNORDERED_MAP -#include + #include #endif #if VMA_USE_STL_LIST -#include + #include #endif /* @@ -726,120 +724,172 @@ remove them if not needed. #include // for aligned_alloc() #endif - -#ifdef _DEBUG - // Normal assert to check for programmer's errors, especially in Debug configuration. - #define VMA_ASSERT(expr) assert(expr) - // Assert that will be called very often, like inside data structures e.g. operator[]. - // Making it non-empty can make program slow. - #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) -#else - #define VMA_ASSERT(expr) - #define VMA_HEAVY_ASSERT(expr) +// Normal assert to check for programmer's errors, especially in Debug configuration. +#ifndef VMA_ASSERT + #ifdef _DEBUG + #define VMA_ASSERT(expr) assert(expr) + #else + #define VMA_ASSERT(expr) + #endif #endif -// Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. -#define VMA_NULL nullptr - -#define VMA_ALIGN_OF(type) (__alignof(type)) - -#if defined(_WIN32) - #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) - #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) -#else - #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) - #define VMA_SYSTEM_FREE(ptr) free(ptr) +// 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 _DEBUG + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) + #endif #endif -#define VMA_MIN(v1, v2) (std::min((v1), (v2))) -#define VMA_MAX(v1, v2) (std::max((v1), (v2))) -#define VMA_SWAP(v1, v2) std::swap((v1), (v2)) +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr +#endif -// You can just comment this out to use internal sorting implementation. -//#define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#ifndef VMA_ALIGN_OF + #define VMA_ALIGN_OF(type) (__alignof(type)) +#endif -#define VMA_DEBUG_LOG(format, ...) -/* -#define VMA_DEBUG_LOG(format, ...) do { \ - printf(format, __VA_ARGS__); \ - printf("\n"); \ -} while(false) -*/ +#ifndef VMA_SYSTEM_ALIGNED_MALLOC + #if defined(_WIN32) + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment))) + #else + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) )) + #endif +#endif +#ifndef VMA_SYSTEM_FREE + #if defined(_WIN32) + #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr) + #else + #define VMA_SYSTEM_FREE(ptr) free(ptr) + #endif +#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 + #define VMA_DEBUG_LOG(format, ...) + /* + #define VMA_DEBUG_LOG(format, ...) do { \ + printf(format, __VA_ARGS__); \ + printf("\n"); \ + } while(false) + */ +#endif + +// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. #if VMA_STATS_STRING_ENABLED + static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num) + { + _ultoa_s(num, outStr, strLen, 10); + } + static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) + { + _ui64toa_s(num, outStr, strLen, 10); + } +#endif -static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num) -{ - _ultoa_s(num, outStr, strLen, 10); -} -static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num) -{ - _ui64toa_s(num, outStr, strLen, 10); -} +#ifndef VMA_MUTEX + class VmaMutex + { + public: + VmaMutex() { } + ~VmaMutex() { } + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex +#endif -#endif // #if VMA_STATS_STRING_ENABLED +#ifndef VMA_BEST_FIT + /** + Main parameter for function assessing how good is a free suballocation for a new + allocation request. -class VmaMutex -{ -public: - VmaMutex() { } - ~VmaMutex() { } - void Lock() { m_Mutex.lock(); } - void Unlock() { m_Mutex.unlock(); } -private: - std::mutex m_Mutex; -}; + - Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the + size of requested allocations as possible. + - Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as + possible. -/* -Main parameter for function assessing how good is a free suballocation for a new -allocation request. + Experiments in special testing environment showed that Best-Fit algorithm is + better. + */ + #define VMA_BEST_FIT (1) +#endif -- Set to true to use Best-Fit algorithm - prefer smaller blocks, as close to the - size of requested allocations as possible. -- Set to false to use Worst-Fit algorithm - prefer larger blocks, as large as - possible. +#ifndef VMA_DEBUG_ALWAYS_OWN_MEMORY + /** + Every object will have its own allocation. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_OWN_MEMORY (0) +#endif -Experiments in special testing environment showed that Best-Fit algorithm is -better. -*/ -static const bool VMA_BEST_FIT = true; +#ifndef VMA_DEBUG_ALIGNMENT + /** + Minimum alignment of all suballocations, in bytes. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_ALIGNMENT (1) +#endif -/* -Every object will have its own allocation. -Enable for debugging purposes only. -*/ -static const bool VMA_DEBUG_ALWAYS_OWN_MEMORY = false; +#ifndef VMA_DEBUG_MARGIN + /** + Minimum margin between suballocations, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif -/* -Minimum alignment of all suballocations, in bytes. -Set to more than 1 for debugging purposes only. Must be power of two. -*/ -static const VkDeviceSize VMA_DEBUG_ALIGNMENT = 1; +#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 -/* -Minimum margin between suballocations, in bytes. -Set nonzero for debugging purposes only. -*/ -static const VkDeviceSize VMA_DEBUG_MARGIN = 0; +#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 -/* -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 +#ifndef VMA_SMALL_HEAP_MAX_SIZE + /// Maximum size of a memory heap in Vulkan to consider it "small". + #define VMA_SMALL_HEAP_MAX_SIZE (512 * 1024 * 1024) +#endif -/* -Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. -Set to more than 1 for debugging purposes only. Must be power of two. -*/ -static const VkDeviceSize VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY = 1; +#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 (256 * 1024 * 1024) +#endif -// Maximum size of a memory heap in Vulkan to consider it "small". -static const VkDeviceSize VMA_SMALL_HEAP_MAX_SIZE = 512 * 1024 * 1024; -// Default size of a block allocated as single VkDeviceMemory from a "large" heap. -static const VkDeviceSize VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE = 256 * 1024 * 1024; -// Default size of a block allocated as single VkDeviceMemory from a "small" heap. -static const VkDeviceSize VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE = 64 * 1024 * 1024; +#ifndef VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE + /// Default size of a block allocated as single VkDeviceMemory from a "small" heap. + #define VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE (64 * 1024 * 1024) +#endif /******************************************************************************* END OF CONFIGURATION @@ -989,15 +1039,15 @@ static inline bool VmaIsBufferImageGranularityConflict( struct VmaMutexLock { public: - VmaMutexLock(VmaMutex& mutex) : m_Mutex(mutex) { mutex.Lock(); } + VmaMutexLock(VMA_MUTEX& mutex) : m_Mutex(mutex) { mutex.Lock(); } ~VmaMutexLock() { m_Mutex.Unlock(); } private: - VmaMutex& m_Mutex; + VMA_MUTEX& m_Mutex; }; #if VMA_DEBUG_GLOBAL_MUTEX - static VmaMutex gDebugGlobalMutex; + static VMA_MUTEX gDebugGlobalMutex; #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex); #else #define VMA_DEBUG_GLOBAL_MUTEX_LOCK @@ -2341,12 +2391,12 @@ struct VmaAllocator_T hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */ bool m_HasEmptyBlock[VK_MAX_MEMORY_TYPES]; - VmaMutex m_BlocksMutex[VK_MAX_MEMORY_TYPES]; + VMA_MUTEX m_BlocksMutex[VK_MAX_MEMORY_TYPES]; // Each vector is sorted by memory (handle value). typedef VmaVector< VmaAllocation, VmaStlAllocator > AllocationVectorType; AllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT]; - VmaMutex m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES]; + VMA_MUTEX m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES]; VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); ~VmaAllocator_T(); @@ -2361,7 +2411,7 @@ struct VmaAllocator_T VkDeviceSize GetBufferImageGranularity() const { return VMA_MAX( - VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY, + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), m_PhysicalDeviceProperties.limits.bufferImageGranularity); } @@ -2914,7 +2964,7 @@ bool VmaBlock::CheckAllocation( *pOffset += VMA_DEBUG_MARGIN; // Apply alignment. - const VkDeviceSize alignment = VMA_MAX(allocAlignment, VMA_DEBUG_ALIGNMENT); + const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast(VMA_DEBUG_ALIGNMENT)); *pOffset = VmaAlignUp(*pOffset, alignment); // Check previous suballocations for BufferImageGranularity conflicts. @@ -3828,9 +3878,9 @@ VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : } m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? - pCreateInfo->preferredLargeHeapBlockSize : VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; + pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ? - pCreateInfo->preferredSmallHeapBlockSize : VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE; + pCreateInfo->preferredSmallHeapBlockSize : static_cast(VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE); vkGetPhysicalDeviceProperties(m_PhysicalDevice, &m_PhysicalDeviceProperties); vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &m_MemProps); From 4184ee53bd14c460d72474639ff712a541ab7c81 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 11 Jul 2017 14:42:45 +0200 Subject: [PATCH 05/11] Updated documentation. --- src/vk_mem_alloc.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 60d3de1..d343003 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -118,7 +118,14 @@ When you want to create a buffer or image: memReq.usage = VMA_MEMORY_USAGE_GPU_ONLY; VkBuffer buffer; - vmaCreateBuffer(allocator, &bufferInfo, &memReq, &buffer, nullptr, nullptr); + VmaAllocation allocation; + vmaCreateBuffer(allocator, &bufferInfo, &memReq, &buffer, &allocation, nullptr); + +Don't forget to destroy your objects: + + + vmaDestroyBuffer(allocator, buffer, allocation); + vmaDestroyAllocator(allocator); \section configuration Configuration From a59788d60dfb2f8677128d086e2ccb0ffd01a147 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 11 Jul 2017 14:43:43 +0200 Subject: [PATCH 06/11] Correct wording of comment about required flags The word "subset" is used when it should be "superset" instead. --- src/vk_mem_alloc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index d343003..acc0692 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -4870,7 +4870,7 @@ VkResult vmaFindMemoryTypeIndex( uint32_t preferredFlags = pMemoryRequirements->preferredFlags; if(preferredFlags == 0) preferredFlags = requiredFlags; - // preferredFlags, if not 0, must be subset of requiredFlags. + // preferredFlags, if not 0, must be a superset of requiredFlags. VMA_ASSERT((requiredFlags & ~preferredFlags) == 0); // Convert usage to requiredFlags and preferredFlags. From f30ee85a15e360b57fa21ef8c903641f4d78d470 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 11 Jul 2017 15:00:11 +0200 Subject: [PATCH 07/11] Small fix in VmaBlock::Free (thanks @dylanede for pointing this!) --- src/vk_mem_alloc.h | 40 +++++++++------------------------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index acc0692..9ab239a 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -3146,42 +3146,20 @@ void VmaBlock::FreeSuballocation(VmaSuballocationList::iterator suballocItem) void VmaBlock::Free(const VmaAllocation allocation) { - // If suballocation to free has offset smaller than half of allocation size, search forward. - // Otherwise search backward. const VkDeviceSize allocationOffset = allocation->GetOffset(); - const bool forwardDirection = allocationOffset < (m_Size / 2); - if(forwardDirection) + for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); + suballocItem != m_Suballocations.end(); + ++suballocItem) { - for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); - suballocItem != m_Suballocations.end(); - ++suballocItem) + VmaSuballocation& suballoc = *suballocItem; + if(suballoc.offset == allocationOffset) { - VmaSuballocation& suballoc = *suballocItem; - if(suballoc.offset == allocationOffset) - { - FreeSuballocation(suballocItem); - VMA_HEAVY_ASSERT(Validate()); - return; - } + FreeSuballocation(suballocItem); + VMA_HEAVY_ASSERT(Validate()); + return; } - VMA_ASSERT(0 && "Not found!"); - } - else - { - for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin(); - suballocItem != m_Suballocations.end(); - ++suballocItem) - { - VmaSuballocation& suballoc = *suballocItem; - if(suballoc.offset == allocationOffset) - { - FreeSuballocation(suballocItem); - VMA_HEAVY_ASSERT(Validate()); - return; - } - } - VMA_ASSERT(0 && "Not found!"); } + VMA_ASSERT(0 && "Not found!"); } #if VMA_STATS_STRING_ENABLED From 31341608f4a3879f00bb07d7658584f624dcd657 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 11 Jul 2017 15:21:10 +0200 Subject: [PATCH 08/11] Added VmaAllocatorCreateInfo::flags, VmaAllocatorFlags, VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT. --- src/vk_mem_alloc.h | 75 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h index 9ab239a..7c600a4 100644 --- a/src/vk_mem_alloc.h +++ b/src/vk_mem_alloc.h @@ -143,8 +143,19 @@ Vulkan, as well as used by the library itself to make any CPU-side allocations. \section thread_safety Thread safety -All calls to functions that take VmaAllocator as first parameter are safe to -call from multiple threads simultaneously, synchronized internally when needed. +- The library has no global state, so separate VmaAllocator objects can be used + independently. +- By default, all calls to functions that take VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. +- When the allocator is created with VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such VmaAllocator object must be + synchronized externally. +- Access to a VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaDefragment() from different + threads at the same time if you pass the same VmaAllocation object to these + functions. + */ #include @@ -181,9 +192,23 @@ typedef struct VmaDeviceMemoryCallbacks { PFN_vmaFreeDeviceMemoryFunction pfnFree; } VmaDeviceMemoryCallbacks; +/// Flags for created VmaAllocator. +typedef enum VmaAllocatorFlagBits { + /** \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_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + + VMA_ALLOCATOR_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocatorFlagBits; +typedef VkFlags VmaAllocatorFlags; + /// Description of a Allocator to be created. typedef struct VmaAllocatorCreateInfo { + /// Flags for created allocator. Use VmaAllocatorFlagBits enum. + VmaAllocatorFlags flags; /// Vulkan physical device. /** It must be valid throughout whole lifetime of created Allocator. */ VkPhysicalDevice physicalDevice; @@ -1046,11 +1071,25 @@ static inline bool VmaIsBufferImageGranularityConflict( struct VmaMutexLock { public: - VmaMutexLock(VMA_MUTEX& mutex) : m_Mutex(mutex) { mutex.Lock(); } - ~VmaMutexLock() { m_Mutex.Unlock(); } + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if(m_pMutex) + { + m_pMutex->Lock(); + } + } + + ~VmaMutexLock() + { + if(m_pMutex) + { + m_pMutex->Unlock(); + } + } private: - VMA_MUTEX& m_Mutex; + VMA_MUTEX* m_pMutex; }; #if VMA_DEBUG_GLOBAL_MUTEX @@ -2380,6 +2419,7 @@ private: // Main allocator object. struct VmaAllocator_T { + bool m_UseMutex; VkDevice m_hDevice; bool m_AllocationCallbacksSpecified; VkAllocationCallbacks m_AllocationCallbacks; @@ -3837,6 +3877,7 @@ bool VmaDefragmentator::MoveMakesSense( // VmaAllocator_T VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : + m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT) == 0), m_PhysicalDevice(pCreateInfo->physicalDevice), m_hDevice(pCreateInfo->device), m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), @@ -3945,7 +3986,7 @@ VkResult VmaAllocator_T::AllocateMemoryOfType( { uint32_t blockVectorType = VmaMemoryRequirementFlagsToBlockVectorType(vmaMemReq.flags); - VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex); VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; VMA_ASSERT(blockVector); @@ -4142,7 +4183,7 @@ VkResult VmaAllocator_T::AllocateOwnMemory( // Register it in m_pOwnAllocations. { - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex); AllocationVectorType* pOwnAllocations = m_pOwnAllocations[memTypeIndex][map ? VMA_BLOCK_VECTOR_TYPE_MAPPED : VMA_BLOCK_VECTOR_TYPE_UNMAPPED]; VMA_ASSERT(pOwnAllocations); VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data(); @@ -4227,7 +4268,7 @@ void VmaAllocator_T::FreeMemory(const VmaAllocation allocation) const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); const VMA_BLOCK_VECTOR_TYPE blockVectorType = allocation->GetBlockVectorType(); { - VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex); VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; VmaBlock* pBlock = allocation->GetBlock(); @@ -4280,7 +4321,7 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats) for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock allocationsLock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock allocationsLock(m_BlocksMutex[memTypeIndex], m_UseMutex); const uint32_t heapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex; for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) { @@ -4313,7 +4354,7 @@ void VmaAllocator_T::UnmapPersistentlyMappedMemory() { // Process OwnAllocations. { - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex); AllocationVectorType* pOwnAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; for(size_t ownAllocIndex = pOwnAllocationsVector->size(); ownAllocIndex--; ) { @@ -4324,7 +4365,7 @@ void VmaAllocator_T::UnmapPersistentlyMappedMemory() // Process normal Allocations. { - VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex); VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; pBlockVector->UnmapPersistentlyMappedMemory(); } @@ -4350,7 +4391,7 @@ VkResult VmaAllocator_T::MapPersistentlyMappedMemory() { // Process OwnAllocations. { - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex); AllocationVectorType* pAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; for(size_t ownAllocIndex = 0, ownAllocCount = pAllocationsVector->size(); ownAllocIndex < ownAllocCount; ++ownAllocIndex) { @@ -4361,7 +4402,7 @@ VkResult VmaAllocator_T::MapPersistentlyMappedMemory() // Process normal Allocations. { - VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex); VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED]; VkResult localResult = pBlockVector->MapPersistentlyMappedMemory(); if(localResult != VK_SUCCESS) @@ -4459,7 +4500,7 @@ VkResult VmaAllocator_T::Defragment( // Only HOST_VISIBLE memory types can be defragmented. if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) { - VmaMutexLock lock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex); for(uint32_t blockVectorType = 0; (blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT) && (result == VK_SUCCESS); @@ -4538,7 +4579,7 @@ void VmaAllocator_T::FreeOwnMemory(VmaAllocation allocation) const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); { - VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]); + VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex); AllocationVectorType* const pOwnAllocations = m_pOwnAllocations[memTypeIndex][allocation->GetBlockVectorType()]; VMA_ASSERT(pOwnAllocations); VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data(); @@ -4586,7 +4627,7 @@ void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb) bool ownAllocationsStarted = false; for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock ownAllocationsLock(m_OwnAllocationsMutex[memTypeIndex]); + VmaMutexLock ownAllocationsLock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex); for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) { AllocationVectorType* const pOwnAllocVector = m_pOwnAllocations[memTypeIndex][blockVectorType]; @@ -4631,7 +4672,7 @@ void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb) bool allocationsStarted = false; for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) { - VmaMutexLock globalAllocationsLock(m_BlocksMutex[memTypeIndex]); + VmaMutexLock globalAllocationsLock(m_BlocksMutex[memTypeIndex], m_UseMutex); for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType) { if(m_pBlockVectors[memTypeIndex][blockVectorType]->IsEmpty() == false) From 988ac4c05d34a4478af146d236244c82b02e289a Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Tue, 11 Jul 2017 15:22:12 +0200 Subject: [PATCH 09/11] Updated version number to 2.0.0-alpha.2. Regenerated Doxygen documentation. --- docs/html/annotated.html | 12 +- docs/html/classes.html | 8 +- docs/html/functions.html | 98 +++- docs/html/functions_vars.html | 98 +++- docs/html/globals.html | 87 +++- docs/html/globals_enum.html | 6 + docs/html/globals_eval.html | 18 + docs/html/globals_func.html | 35 +- docs/html/globals_type.html | 30 ++ docs/html/group__general.html | 128 ++++++ docs/html/group__layer1.html | 75 ++++ docs/html/group__layer2.html | 420 +++++++++++++++--- docs/html/group__layer3.html | 76 ++-- docs/html/index.html | 23 +- docs/html/menudata.js | 27 +- docs/html/search/all_0.js | 3 +- docs/html/search/all_1.js | 3 +- docs/html/search/all_2.js | 4 +- docs/html/search/all_3.js | 4 +- docs/html/search/all_4.js | 3 +- docs/html/search/all_5.js | 4 +- docs/html/search/all_6.js | 5 +- docs/html/search/all_7.js | 6 +- docs/html/search/all_8.js | 13 +- docs/html/search/all_9.js | 5 +- docs/html/search/all_a.js | 6 +- docs/html/search/all_b.js | 8 +- docs/html/search/all_c.js | 40 +- docs/html/search/all_d.html | 26 ++ docs/html/search/all_d.js | 55 +++ docs/html/search/classes_0.js | 4 + docs/html/search/enums_0.js | 2 + docs/html/search/enumvalues_0.js | 6 + docs/html/search/functions_0.js | 25 +- docs/html/search/searchdata.js | 6 +- docs/html/search/typedefs_0.js | 6 +- docs/html/search/typedefs_1.html | 26 ++ docs/html/search/typedefs_1.js | 15 + docs/html/search/variables_0.js | 3 +- docs/html/search/variables_1.js | 3 +- docs/html/search/variables_2.js | 5 +- docs/html/search/variables_3.js | 2 +- docs/html/search/variables_4.js | 5 +- docs/html/search/variables_5.js | 6 +- docs/html/search/variables_6.js | 11 +- docs/html/search/variables_7.js | 5 +- docs/html/search/variables_8.js | 6 +- docs/html/search/variables_9.js | 8 +- docs/html/search/variables_a.html | 26 ++ docs/html/search/variables_a.js | 10 + .../struct_vma_allocation_info-members.html | 82 ++++ docs/html/struct_vma_allocation_info.html | 212 +++++++++ ...uct_vma_allocator_create_info-members.html | 4 +- .../struct_vma_allocator_create_info.html | 39 ++ ...ruct_vma_defragmentation_info-members.html | 78 ++++ .../html/struct_vma_defragmentation_info.html | 131 ++++++ ...uct_vma_defragmentation_stats-members.html | 80 ++++ .../struct_vma_defragmentation_stats.html | 167 +++++++ ...t_vma_device_memory_callbacks-members.html | 78 ++++ .../struct_vma_device_memory_callbacks.html | 130 ++++++ ...truct_vma_memory_requirements-members.html | 6 +- docs/html/struct_vma_memory_requirements.html | 55 +-- docs/html/vk__mem__alloc_8h.html | 115 ++++- docs/html/vk__mem__alloc_8h_source.html | 133 ++++-- src/vk_mem_alloc.h | 2 +- 65 files changed, 2469 insertions(+), 349 deletions(-) create mode 100644 docs/html/search/all_d.html create mode 100644 docs/html/search/all_d.js create mode 100644 docs/html/search/typedefs_1.html create mode 100644 docs/html/search/typedefs_1.js create mode 100644 docs/html/search/variables_a.html create mode 100644 docs/html/search/variables_a.js create mode 100644 docs/html/struct_vma_allocation_info-members.html create mode 100644 docs/html/struct_vma_allocation_info.html create mode 100644 docs/html/struct_vma_defragmentation_info-members.html create mode 100644 docs/html/struct_vma_defragmentation_info.html create mode 100644 docs/html/struct_vma_defragmentation_stats-members.html create mode 100644 docs/html/struct_vma_defragmentation_stats.html create mode 100644 docs/html/struct_vma_device_memory_callbacks-members.html create mode 100644 docs/html/struct_vma_device_memory_callbacks.html diff --git a/docs/html/annotated.html b/docs/html/annotated.html index 3e5adda..20a707d 100644 --- a/docs/html/annotated.html +++ b/docs/html/annotated.html @@ -64,10 +64,14 @@ $(function() {
Here are the classes, structs, unions and interfaces with brief descriptions:
- - - - + + + + + + + +
 CVmaAllocatorCreateInfoDescription of a Allocator to be created
 CVmaMemoryRequirements
 CVmaStatInfo
 CVmaStatsGeneral statistics from current state of Allocator
 CVmaAllocationInfoParameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo()
 CVmaAllocatorCreateInfoDescription of a Allocator to be created
 CVmaDefragmentationInfoOptional configuration parameters to be passed to function vmaDefragment()
 CVmaDefragmentationStatsStatistics returned by function vmaDefragment()
 CVmaDeviceMemoryCallbacksSet of callbacks that the library will call for vkAllocateMemory and vkFreeMemory
 CVmaMemoryRequirements
 CVmaStatInfo
 CVmaStatsGeneral statistics from current state of Allocator
diff --git a/docs/html/classes.html b/docs/html/classes.html index fb0f1ec..67a1270 100644 --- a/docs/html/classes.html +++ b/docs/html/classes.html @@ -65,10 +65,10 @@ $(function() { - - - + + + +
  v  
-
VmaMemoryRequirements   VmaStats   
VmaStatInfo   
VmaAllocatorCreateInfo   
VmaAllocatorCreateInfo   VmaDefragmentationStats   VmaMemoryRequirements   VmaStats   
VmaDefragmentationInfo   VmaDeviceMemoryCallbacks   VmaStatInfo   
VmaAllocationInfo   
diff --git a/docs/html/functions.html b/docs/html/functions.html index 89c1389..a924d1f 100644 --- a/docs/html/functions.html +++ b/docs/html/functions.html @@ -58,31 +58,92 @@ $(function() {
-
Here is a list of all class members with links to the classes they belong to:
+ + +

◆ VmaAllocatorFlagBits

+ +
+
+ + + + +
typedef enum VmaAllocatorFlagBits VmaAllocatorFlagBits
+
+ +

Flags for created VmaAllocator.

+ +
+
+ +

◆ VmaAllocatorFlags

+ +
+
+ + + + +
typedef VkFlags VmaAllocatorFlags
+
+ +
+
+ +

◆ VmaDeviceMemoryCallbacks

+ +
+
+ +

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.

+
@@ -161,6 +266,29 @@ Functions
+
+ +

Enumeration Type Documentation

+ +

◆ VmaAllocatorFlagBits

+ +
+
+ + + + +
enum VmaAllocatorFlagBits
+
+ +

Flags for created VmaAllocator.

+ + + +
Enumerator
VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT 

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_FLAG_BITS_MAX_ENUM 
+

Function Documentation

diff --git a/docs/html/group__layer1.html b/docs/html/group__layer1.html index b27b4a0..a1f5fc5 100644 --- a/docs/html/group__layer1.html +++ b/docs/html/group__layer1.html @@ -77,6 +77,11 @@ Classes Typedefs typedef enum VmaMemoryUsage VmaMemoryUsage   +typedef enum VmaMemoryRequirementFlagBits VmaMemoryRequirementFlagBits + Flags to be passed as VmaMemoryRequirements::flags. More...
+  +typedef VkFlags VmaMemoryRequirementFlags +  typedef struct VmaMemoryRequirements VmaMemoryRequirements   @@ -93,6 +98,13 @@ Enumerations
} + + +
 
enum  VmaMemoryRequirementFlagBits { VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT = 0x00000001, +VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT = 0x00000002, +VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT = 0x00000004, +VMA_MEMORY_REQUIREMENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF + }
 Flags to be passed as VmaMemoryRequirements::flags. More...
 
@@ -101,6 +113,36 @@ Functions

Functions

Detailed Description

Typedef Documentation

+ +

◆ VmaMemoryRequirementFlagBits

+ +
+
+ +

Flags to be passed as VmaMemoryRequirements::flags.

+ +
+
+ +

◆ VmaMemoryRequirementFlags

+ +
+
+ + + + +
typedef VkFlags VmaMemoryRequirementFlags
+
+ +
+

◆ VmaMemoryRequirements

@@ -130,6 +172,36 @@ Functions

Enumeration Type Documentation

+ +

◆ VmaMemoryRequirementFlagBits

+ +
+
+ +

Flags to be passed as VmaMemoryRequirements::flags.

+ + + + + +
Enumerator
VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT 

Set this flag if the allocation should have its own memory block.

+

Use it for special, big resources, like fullscreen images used as attachments.

+

This flag must also be used for host visible resources that you want to map simultaneously because otherwise they might end up as regions of the same VkDeviceMemory, while mapping same VkDeviceMemory multiple times is illegal.

+
VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT 

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.

+

It makes no sense to set VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT and VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT at the same time.

+
VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT 

Set to use a memory that will be persistently mapped and retrieve pointer to it.

+

Pointer to mapped memory will be returned through ppMappedData. You cannot map the memory on your own as multiple maps of a single VkDeviceMemory are illegal.

+
VMA_MEMORY_REQUIREMENT_FLAG_BITS_MAX_ENUM 
+ +
+

◆ VmaMemoryUsage

@@ -147,10 +219,13 @@ Functions VMA_MEMORY_USAGE_GPU_ONLY 

Memory will be used on device only, no need to be mapped on host.

VMA_MEMORY_USAGE_CPU_ONLY 

Memory will be mapped on host. Could be used for transfer to device.

+

Guarantees to be HOST_VISIBLE and HOST_COHERENT.

VMA_MEMORY_USAGE_CPU_TO_GPU 

Memory will be used for frequent (dynamic) updates from host and reads on device.

+

Guarantees to be HOST_VISIBLE.

VMA_MEMORY_USAGE_GPU_TO_CPU 

Memory will be used for writing on device and readback on host.

+

Guarantees to be HOST_VISIBLE.

VMA_MEMORY_USAGE_MAX_ENUM  diff --git a/docs/html/group__layer2.html b/docs/html/group__layer2.html index 18ab2b6..c03ac57 100644 --- a/docs/html/group__layer2.html +++ b/docs/html/group__layer2.html @@ -59,34 +59,124 @@ $(function() {
Layer 2 Allocating Memory
+ + + + + + + + + + +

+Classes

struct  VmaAllocationInfo
 Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). More...
 
struct  VmaDefragmentationInfo
 Optional configuration parameters to be passed to function vmaDefragment(). More...
 
struct  VmaDefragmentationStats
 Statistics returned by function vmaDefragment(). More...
 
+ + + + + + + + + + +

+Typedefs

typedef struct VmaAllocationInfo VmaAllocationInfo
 Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). More...
 
typedef struct VmaDefragmentationInfo VmaDefragmentationInfo
 Optional configuration parameters to be passed to function vmaDefragment(). More...
 
typedef struct VmaDefragmentationStats VmaDefragmentationStats
 Statistics returned by function vmaDefragment(). More...
 
- - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Functions

VkResult vmaAllocateMemory (VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
 General purpose memory allocation. More...
 
VkResult vmaAllocateMemoryForBuffer (VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
 
VkResult vmaAllocateMemoryForImage (VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
 Function similar to vmaAllocateMemoryForBuffer(). More...
 
void vmaFreeMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
 Frees memory previously allocated using vmaAllocateMemoryForBuffer() or vmaAllocateMemoryForImage(). More...
 
VkResult vmaMapMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory, void **ppData)
 
void vmaUnmapMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
 
VkResult vmaAllocateMemory (VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
 General purpose memory allocation. More...
 
VkResult vmaAllocateMemoryForBuffer (VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
 
VkResult vmaAllocateMemoryForImage (VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
 Function similar to vmaAllocateMemoryForBuffer(). More...
 
void vmaFreeMemory (VmaAllocator allocator, VmaAllocation allocation)
 Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). More...
 
void vmaGetAllocationInfo (VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
 Returns current information about specified allocation. More...
 
void vmaSetAllocationUserData (VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
 Sets pUserData in given allocation to new value. More...
 
VkResult vmaMapMemory (VmaAllocator allocator, VmaAllocation allocation, void **ppData)
 
void vmaUnmapMemory (VmaAllocator allocator, VmaAllocation allocation)
 
void vmaUnmapPersistentlyMappedMemory (VmaAllocator allocator)
 Unmaps persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. More...
 
VkResult vmaMapPersistentlyMappedMemory (VmaAllocator allocator)
 Maps back persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. More...
 
VkResult vmaDefragment (VmaAllocator allocator, VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
 Compacts memory by moving allocations. More...
 

Detailed Description

+

Typedef Documentation

+ +

◆ VmaAllocationInfo

+ +
+
+ + + + +
typedef struct VmaAllocationInfo VmaAllocationInfo
+
+ +

Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().

+ +
+
+ +

◆ VmaDefragmentationInfo

+ +
+
+ +

Optional configuration parameters to be passed to function vmaDefragment().

+ +
+
+ +

◆ VmaDefragmentationStats

+ +
+
+ +

Statistics returned by function vmaDefragment().

+ +
+

Function Documentation

- -

◆ vmaAllocateMemory()

+ +

◆ vmaAllocateMemory()

@@ -112,14 +202,14 @@ Functions - VkMappedMemoryRange *  - pMemory, + VmaAllocation *  + pAllocation, - uint32_t *  - pMemoryTypeIndex  + VmaAllocationInfo *  + pAllocationInfo  @@ -132,19 +222,18 @@ Functions

General purpose memory allocation.

Parameters
- - + +
[out]pMemoryAllocated memory.
[out]pMemoryTypeIndexOptional. Index of memory type that has been chosen for this allocation.
[out]pAllocationHandle to allocated memory.
[out]pAllocationInfoOptional. Information about allocated memory. It can be later fetched using function VmaGetAllocationInfo().
-

You should free the memory using vmaFreeMemory().

-

All allocated memory is also automatically freed in vmaDestroyAllocator().

-

It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), vmaCreateBuffer(), vmaCreateImage() instead whenever possible.

+

You should free the memory using vmaFreeMemory().

+

It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), vmaCreateBuffer(), vmaCreateImage() instead whenever possible.

- -

◆ vmaAllocateMemoryForBuffer()

+ +

◆ vmaAllocateMemoryForBuffer()

@@ -170,14 +259,14 @@ Functions - VkMappedMemoryRange *  - pMemory, + VmaAllocation *  + pAllocation, - uint32_t *  - pMemoryTypeIndex  + VmaAllocationInfo *  + pAllocationInfo  @@ -188,17 +277,17 @@ Functions
Parameters
- + +
[out]pMemoryTypeIndexOptional. Pass null if you don't need this information.
[out]pAllocationHandle to allocated memory.
[out]pAllocationInfoOptional. Information about allocated memory. It can be later fetched using function VmaGetAllocationInfo().
-

You should free the memory using vmaFreeMemory().

-

All allocated memory is also automatically freed in vmaDestroyAllocator().

+

You should free the memory using vmaFreeMemory().

- -

◆ vmaAllocateMemoryForImage()

+ +

◆ vmaAllocateMemoryForImage()

@@ -224,14 +313,14 @@ Functions - VkMappedMemoryRange *  - pMemory, + VmaAllocation *  + pAllocation, - uint32_t *  - pMemoryTypeIndex  + VmaAllocationInfo *  + pAllocationInfo  @@ -241,12 +330,113 @@ Functions
-

Function similar to vmaAllocateMemoryForBuffer().

+

Function similar to vmaAllocateMemoryForBuffer().

- -

◆ vmaFreeMemory()

+ +

◆ vmaDefragment()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
VkResult vmaDefragment (VmaAllocator allocator,
VmaAllocation * pAllocations,
size_t allocationCount,
VkBool32 * pAllocationsChanged,
const VmaDefragmentationInfopDefragmentationInfo,
VmaDefragmentationStatspDefragmentationStats 
)
+
+ +

Compacts memory by moving allocations.

+
Parameters
+ + + + + + +
pAllocationsArray of allocations that can be moved during this compation.
allocationCountNumber of elements in pAllocations and pAllocationsChanged arrays.
[out]pAllocationsChangedArray of boolean values that will indicate whether matching allocation in pAllocations array has been moved. This parameter is optional. Pass null if you don't need this information.
pDefragmentationInfoConfiguration parameters. Optional - pass null to use default values.
[out]pDefragmentationStatsStatistics returned by the function. Optional - pass null if you don't need this information.
+
+
+
Returns
VK_SUCCESS if completed, VK_INCOMPLETE if succeeded but didn't make all possible optimizations because limits specified in pDefragmentationInfo have been reached, negative error code in case of error.
+

This function works by moving allocations to different places (different VkDeviceMemory objects and/or different offsets) in order to optimize memory usage. Only allocations that are in pAllocations array can be moved. All other allocations are considered nonmovable in this call. Basic rules:

+
    +
  • Only allocations made in memory types that have VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT flag can be compacted. You may pass other allocations but it makes no sense - these will never be moved.
  • +
  • You may pass allocations made with VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT but it makes no sense - they will never be moved.
  • +
  • Both allocations made with or without VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT flag can be compacted. If not persistently mapped, memory will be mapped temporarily inside this function if needed, so it shouldn't be mapped by you for the time of this call.
  • +
  • You must not pass same VmaAllocation object multiple times in pAllocations array.
  • +
+

The function also frees empty VkDeviceMemory blocks.

+

After allocation has been moved, its VmaAllocationInfo::deviceMemory and/or VmaAllocationInfo::offset changes. You must query them again using vmaGetAllocationInfo() if you need them.

+

If an allocation has been moved, data in memory is copied to new place automatically, but if it was bound to a buffer or an image, you must destroy that object yourself, create new one and bind it to the new memory pointed by the allocation. You must use vkDestroyBuffer(), vkDestroyImage(), vkCreateBuffer(), vkCreateImage() for that purpose and NOT vmaDestroyBuffer(), vmaDestroyImage(), vmaCreateBuffer(), vmaCreateImage()! Example:

+
VkDevice device = ...;
+VmaAllocator allocator = ...;
+std::vector<VkBuffer> buffers = ...;
+std::vector<VmaAllocation> allocations = ...;
+
+std::vector<VkBool32> allocationsChanged(allocations.size());
+vmaDefragment(allocator, allocations.data(), allocations.size(), allocationsChanged.data(), nullptr, nullptr);
+
+for(size_t i = 0; i < allocations.size(); ++i)
+{
+    if(allocationsChanged[i])
+    {
+        VmaAllocationInfo allocInfo;
+        vmaGetAllocationInfo(allocator, allocations[i], &allocInfo);
+
+        vkDestroyBuffer(device, buffers[i], nullptr);
+
+        VkBufferCreateInfo bufferInfo = ...;
+        vkCreateBuffer(device, &bufferInfo, nullptr, &buffers[i]);
+
+        .// You can make dummy call to vkGetBufferMemoryRequirements here to silence validation layer warning.
+
+        vkBindBufferMemory(device, buffers[i], allocInfo.deviceMemory, allocInfo.offset);
+    }
+}
+

This function may be time-consuming, so you shouldn't call it too often (like every frame or after every resource creation/destruction), but rater you can call it on special occasions (like when reloading a game level, when you just destroyed a lot of objects).

+ +
+
+ +

◆ vmaFreeMemory()

@@ -260,8 +450,8 @@ Functions - const VkMappedMemoryRange *  - pMemory  + VmaAllocation  + allocation  @@ -271,12 +461,48 @@ Functions
-

Frees memory previously allocated using vmaAllocateMemoryForBuffer() or vmaAllocateMemoryForImage().

+

Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().

- -

◆ vmaMapMemory()

+ +

◆ vmaGetAllocationInfo()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
void vmaGetAllocationInfo (VmaAllocator allocator,
VmaAllocation allocation,
VmaAllocationInfopAllocationInfo 
)
+
+ +

Returns current information about specified allocation.

+ +
+
+ +

◆ vmaMapMemory()

@@ -290,8 +516,8 @@ Functions - const VkMappedMemoryRange *  - pMemory, + VmaAllocation  + allocation, @@ -306,12 +532,71 @@ Functions
-

Feel free to use vkMapMemory on these memory blocks on you own if you want, but just for convenience and to make sure correct offset and size is always specified, usage of vmaMapMemory() / vmaUnmapMemory() is recommended.

+

Feel free to use vkMapMemory on these memory blocks on you own if you want, but just for convenience and to make sure correct offset and size is always specified, usage of vmaMapMemory() / vmaUnmapMemory() is recommended.

+

Do not use it on memory allocated with VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT as multiple maps to same VkDeviceMemory is illegal.

- -

◆ vmaUnmapMemory()

+ +

◆ vmaMapPersistentlyMappedMemory()

+ +
+
+ + + + + + + + +
VkResult vmaMapPersistentlyMappedMemory (VmaAllocator allocator)
+
+ +

Maps back persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL.

+

See vmaUnmapPersistentlyMappedMemory().

+

After this call VmaAllocationInfo::pMappedData of some allocation may have value different than before calling vmaUnmapPersistentlyMappedMemory().

+ +
+
+ +

◆ vmaSetAllocationUserData()

+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
void vmaSetAllocationUserData (VmaAllocator allocator,
VmaAllocation allocation,
void * pUserData 
)
+
+ +

Sets pUserData in given allocation to new value.

+ +
+
+ +

◆ vmaUnmapMemory()

@@ -325,8 +610,8 @@ Functions - const VkMappedMemoryRange *  - pMemory  + VmaAllocation  + allocation  @@ -336,6 +621,29 @@ Functions
+
+
+ +

◆ vmaUnmapPersistentlyMappedMemory()

+ +
+
+ + + + + + + + +
void vmaUnmapPersistentlyMappedMemory (VmaAllocator allocator)
+
+ +

Unmaps persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL.

+

This is optional performance optimization. You should call it on Windows for time of call to vkQueueSubmit and vkQueuePresent, for performance reasons, because of the internal behavior of WDDM.

+

After this call VmaAllocationInfo::pMappedData of some allocations may become null.

+

This call is reference-counted. Memory is mapped again after you call vmaMapPersistentlyMappedMemory() same number of times that you called vmaUnmapPersistentlyMappedMemory().

+
diff --git a/docs/html/group__layer3.html b/docs/html/group__layer3.html index 854b6e2..e7aa1f0 100644 --- a/docs/html/group__layer3.html +++ b/docs/html/group__layer3.html @@ -67,20 +67,20 @@ $(function() { - - - - - - - - - + + + + + + + + +

Functions

VkResult vmaCreateBuffer (VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
 
void vmaDestroyBuffer (VmaAllocator allocator, VkBuffer buffer)
 
VkResult vmaCreateImage (VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
 Function similar to vmaCreateBuffer(). More...
 
void vmaDestroyImage (VmaAllocator allocator, VkImage image)
 
VkResult vmaCreateBuffer (VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
 
void vmaDestroyBuffer (VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
 
VkResult vmaCreateImage (VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
 Function similar to vmaCreateBuffer(). More...
 
void vmaDestroyImage (VmaAllocator allocator, VkImage image, VmaAllocation allocation)
 

Detailed Description

Function Documentation

- -

◆ vmaCreateBuffer()

+ +

◆ vmaCreateBuffer()

@@ -112,14 +112,14 @@ Functions - VkMappedMemoryRange *  - pMemory, + VmaAllocation *  + pAllocation, - uint32_t *  - pMemoryTypeIndex  + VmaAllocationInfo *  + pAllocationInfo  @@ -130,8 +130,9 @@ Functions
Parameters
- - + + +
[out]pMemoryOptional. Pass null if you don't need this information.
[out]pMemoryTypeIndexOptional. Pass null if you don't need this information.
[out]pBufferBuffer that was created.
[out]pAllocationAllocation that was created.
[out]pAllocationInfoOptional. Information about allocated memory. It can be later fetched using function VmaGetAllocationInfo().
@@ -141,13 +142,12 @@ Functions
  • Allocates appropriate memory for it.
  • Binds the buffer/image with the memory.
  • -

    You do not (and should not) pass returned pMemory to vmaFreeMemory. Only calling vmaDestroyBuffer() / vmaDestroyImage() is required for objects created using vmaCreateBuffer() / vmaCreateImage().

    -

    All allocated buffers and images are also automatically destroyed in vmaDestroyAllocator(), along with their memory allocations.

    +

    You do not (and should not) pass returned pMemory to vmaFreeMemory. Only calling vmaDestroyBuffer() / vmaDestroyImage() is required for objects created using vmaCreateBuffer() / vmaCreateImage().

    - -

    ◆ vmaCreateImage()

    + +

    ◆ vmaCreateImage()

    @@ -179,14 +179,14 @@ Functions - VkMappedMemoryRange *  - pMemory, + VmaAllocation *  + pAllocation, - uint32_t *  - pMemoryTypeIndex  + VmaAllocationInfo *  + pAllocationInfo  @@ -196,12 +196,12 @@ Functions
    -

    Function similar to vmaCreateBuffer().

    +

    Function similar to vmaCreateBuffer().

    - -

    ◆ vmaDestroyBuffer()

    + +

    ◆ vmaDestroyBuffer()

    @@ -216,7 +216,13 @@ Functions VkBuffer  - buffer  + buffer, + + + + + VmaAllocation  + allocation  @@ -228,8 +234,8 @@ Functions
    - -

    ◆ vmaDestroyImage()

    + +

    ◆ vmaDestroyImage()

    @@ -244,7 +250,13 @@ Functions VkImage  - image  + image, + + + + + VmaAllocation  + allocation  diff --git a/docs/html/index.html b/docs/html/index.html index e65d94c..20a1a2b 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -62,7 +62,7 @@ $(function() {
    Vulkan Memory Allocator
    -

    Version 1.0.0 (2017-06-16)

    +

    Version 2.0.0-alpha.2 (2017-07-11)

    Members grouped: see Modules.

    All members: see vk_mem_alloc.h.

    @@ -72,7 +72,6 @@ Problem Statement

  • It requires a lot of boilerplate code, just like everything else in Vulkan, because it is a low-level and high-performance API.
  • There is additional level of indirection: VkDeviceMemory is allocated separately from creating VkBuffer/VkImage and they must be bound together. The binding cannot be changed later - resource must be recreated.
  • Driver must be queried for supported memory heaps and memory types. Different IHV-s provide different types of it.
  • -
  • Resources that don't fit in VRAM are not automatically evicted to RAM. Developer must handle out-of-memory errors on his own.
  • It is recommended practice to allocate bigger chunks of memory and assign parts of them to particular resources.
  • @@ -122,7 +121,7 @@ vmaCreateAllocator(&allocatorInfo, &allocator);
    1. Fill VkBufferCreateInfo / VkImageCreateInfo structure.
    2. Fill VmaMemoryRequirements structure.
    3. -
    4. Call vmaCreateBuffer() / vmaCreateImage() to get VkBuffer/VkImage with memory already allocated and bound to it.
    5. +
    6. Call vmaCreateBuffer() / vmaCreateImage() to get VkBuffer/VkImage with memory already allocated and bound to it.
    VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
     bufferInfo.size = myBufferSize;
    @@ -132,19 +131,25 @@ VmaMemoryRequirements memReq = {};
     memReq.usage = VMA_MEMORY_USAGE_GPU_ONLY;
     
     VkBuffer buffer;
    -vmaCreateBuffer(allocator, &bufferInfo, &memReq, &buffer, nullptr, nullptr);
    -

    When no longer needed, destroy your buffer or image using vmaDestroyBuffer() / vmaDestroyImage(). This function would also free memory bound to it.

    -
    vmaDestroyBuffer(allocator, buffer);
    +VmaAllocation allocation;
    +vmaCreateBuffer(allocator, &bufferInfo, &memReq, &buffer, &allocation, nullptr);
    +

    Don't forget to destroy your objects:

    +
    vmaDestroyBuffer(allocator, buffer, allocation);
    +vmaDestroyAllocator(allocator);
     

    Configuration

    -

    Set VMA_STATS_STRING_ENABLED macro in vk_mem_alloc.h to 0 or 1 to disable/enable compilation of code for dumping internal allocator state to string in JSON format.

    -

    Please check "CONFIGURATION" section in vk_mem_alloc.cpp file to find macros and other definitions that you can change to connect the library to your own implementation of basic facilities like assert, min and max functions, mutex etc. C++ STL is used by default, but changing these allows you to get rid of any STL usage if you want, as many game developers tend to do.

    +

    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 etc. C++ STL is used by default, but changing these allows you to get rid of any STL usage if you want, as many game developers tend to do.

    Custom memory allocator

    You can use custom memory allocator 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.

    Thread safety

    -

    All calls to functions that take VmaAllocator as first parameter are safe to call from multiple threads simultaneously, synchronized internally when needed.

    +
      +
    • The library has no global state, so separate VmaAllocator objects can be used independently.
    • +
    • By default, all calls to functions that take VmaAllocator as first parameter are safe to call from multiple threads simultaneously because they are synchronized internally when needed.
    • +
    • When the allocator is created with VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT flag, calls to functions that take such VmaAllocator object must be synchronized externally.
    • +
    • Access to a VmaAllocation object must be externally synchronized. For example, you must not call vmaGetAllocationInfo() and vmaDefragment() from different threads at the same time if you pass the same VmaAllocation object to these functions.
    • +
    + +

    ◆ flags

    + +
    +
    + + + + +
    VmaAllocatorFlags VmaAllocatorCreateInfo::flags
    +
    + +

    Flags for created allocator. Use VmaAllocatorFlagBits enum.

    +
    @@ -124,6 +146,23 @@ Public Attributes

    Custom allocation callbacks.

    Optional, can be null. When specified, will also be used for all CPU-side memory allocations.

    + + + +

    ◆ pDeviceMemoryCallbacks

    + +
    +
    + + + + +
    const VmaDeviceMemoryCallbacks* VmaAllocatorCreateInfo::pDeviceMemoryCallbacks
    +
    + +

    Informative callbacks for vkAllocateMemory, vkFreeMemory.

    +

    Optional, can be null.

    +
    diff --git a/docs/html/struct_vma_defragmentation_info-members.html b/docs/html/struct_vma_defragmentation_info-members.html new file mode 100644 index 0000000..8b5fac6 --- /dev/null +++ b/docs/html/struct_vma_defragmentation_info-members.html @@ -0,0 +1,78 @@ + + + + + + + +Vulkan Memory Allocator: Member List + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    +
    VmaDefragmentationInfo Member List
    +
    +
    + +

    This is the complete list of members for VmaDefragmentationInfo, including all inherited members.

    + + + +
    maxAllocationsToMoveVmaDefragmentationInfo
    maxBytesToMoveVmaDefragmentationInfo
    + + + + diff --git a/docs/html/struct_vma_defragmentation_info.html b/docs/html/struct_vma_defragmentation_info.html new file mode 100644 index 0000000..e4be271 --- /dev/null +++ b/docs/html/struct_vma_defragmentation_info.html @@ -0,0 +1,131 @@ + + + + + + + +Vulkan Memory Allocator: VmaDefragmentationInfo Struct Reference + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    + +
    +
    VmaDefragmentationInfo Struct Reference
    +
    +
    + +

    Optional configuration parameters to be passed to function vmaDefragment(). + More...

    + +

    #include <vk_mem_alloc.h>

    + + + + + + + + +

    +Public Attributes

    VkDeviceSize maxBytesToMove
     Maximum total numbers of bytes that can be copied while moving allocations to different places. More...
     
    uint32_t maxAllocationsToMove
     Maximum number of allocations that can be moved to different place. More...
     
    +

    Detailed Description

    +

    Optional configuration parameters to be passed to function vmaDefragment().

    +

    Member Data Documentation

    + +

    ◆ maxAllocationsToMove

    + +
    +
    + + + + +
    uint32_t VmaDefragmentationInfo::maxAllocationsToMove
    +
    + +

    Maximum number of allocations that can be moved to different place.

    +

    Default is UINT32_MAX, which means no limit.

    + +
    +
    + +

    ◆ maxBytesToMove

    + +
    +
    + + + + +
    VkDeviceSize VmaDefragmentationInfo::maxBytesToMove
    +
    + +

    Maximum total numbers of bytes that can be copied while moving allocations to different places.

    +

    Default is VK_WHOLE_SIZE, which means no limit.

    + +
    +
    +
    The documentation for this struct was generated from the following file: +
    + + + + diff --git a/docs/html/struct_vma_defragmentation_stats-members.html b/docs/html/struct_vma_defragmentation_stats-members.html new file mode 100644 index 0000000..c823a39 --- /dev/null +++ b/docs/html/struct_vma_defragmentation_stats-members.html @@ -0,0 +1,80 @@ + + + + + + + +Vulkan Memory Allocator: Member List + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    +
    VmaDefragmentationStats Member List
    +
    + + + + + diff --git a/docs/html/struct_vma_defragmentation_stats.html b/docs/html/struct_vma_defragmentation_stats.html new file mode 100644 index 0000000..7c92ff4 --- /dev/null +++ b/docs/html/struct_vma_defragmentation_stats.html @@ -0,0 +1,167 @@ + + + + + + + +Vulkan Memory Allocator: VmaDefragmentationStats Struct Reference + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    + +
    +
    VmaDefragmentationStats Struct Reference
    +
    +
    + +

    Statistics returned by function vmaDefragment(). + More...

    + +

    #include <vk_mem_alloc.h>

    + + + + + + + + + + + + + + +

    +Public Attributes

    VkDeviceSize bytesMoved
     Total number of bytes that have been copied while moving allocations to different places. More...
     
    VkDeviceSize bytesFreed
     Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects. More...
     
    uint32_t allocationsMoved
     Number of allocations that have been moved to different places. More...
     
    uint32_t deviceMemoryBlocksFreed
     Number of empty VkDeviceMemory objects that have been released to the system. More...
     
    +

    Detailed Description

    +

    Statistics returned by function vmaDefragment().

    +

    Member Data Documentation

    + +

    ◆ allocationsMoved

    + +
    +
    + + + + +
    uint32_t VmaDefragmentationStats::allocationsMoved
    +
    + +

    Number of allocations that have been moved to different places.

    + +
    +
    + +

    ◆ bytesFreed

    + +
    +
    + + + + +
    VkDeviceSize VmaDefragmentationStats::bytesFreed
    +
    + +

    Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects.

    + +
    +
    + +

    ◆ bytesMoved

    + +
    +
    + + + + +
    VkDeviceSize VmaDefragmentationStats::bytesMoved
    +
    + +

    Total number of bytes that have been copied while moving allocations to different places.

    + +
    +
    + +

    ◆ deviceMemoryBlocksFreed

    + +
    +
    + + + + +
    uint32_t VmaDefragmentationStats::deviceMemoryBlocksFreed
    +
    + +

    Number of empty VkDeviceMemory objects that have been released to the system.

    + +
    +
    +
    The documentation for this struct was generated from the following file: +
    + + + + diff --git a/docs/html/struct_vma_device_memory_callbacks-members.html b/docs/html/struct_vma_device_memory_callbacks-members.html new file mode 100644 index 0000000..10c56e5 --- /dev/null +++ b/docs/html/struct_vma_device_memory_callbacks-members.html @@ -0,0 +1,78 @@ + + + + + + + +Vulkan Memory Allocator: Member List + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    +
    VmaDeviceMemoryCallbacks Member List
    +
    +
    + +

    This is the complete list of members for VmaDeviceMemoryCallbacks, including all inherited members.

    + + + +
    pfnAllocateVmaDeviceMemoryCallbacks
    pfnFreeVmaDeviceMemoryCallbacks
    + + + + diff --git a/docs/html/struct_vma_device_memory_callbacks.html b/docs/html/struct_vma_device_memory_callbacks.html new file mode 100644 index 0000000..edbe682 --- /dev/null +++ b/docs/html/struct_vma_device_memory_callbacks.html @@ -0,0 +1,130 @@ + + + + + + + +Vulkan Memory Allocator: VmaDeviceMemoryCallbacks Struct Reference + + + + + + + + + +
    +
    + + + + + + +
    +
    Vulkan Memory Allocator +
    +
    +
    + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    + +
    +
    VmaDeviceMemoryCallbacks Struct Reference
    +
    +
    + +

    Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory. + More...

    + +

    #include <vk_mem_alloc.h>

    + + + + + + + + +

    +Public Attributes

    PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
     Optional, can be null. More...
     
    PFN_vmaFreeDeviceMemoryFunction pfnFree
     Optional, can be null. More...
     
    +

    Detailed Description

    +

    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.

    +

    Member Data Documentation

    + +

    ◆ pfnAllocate

    + +
    +
    + + + + +
    PFN_vmaAllocateDeviceMemoryFunction VmaDeviceMemoryCallbacks::pfnAllocate
    +
    + +

    Optional, can be null.

    + +
    +
    + +

    ◆ pfnFree

    + +
    +
    + + + + +
    PFN_vmaFreeDeviceMemoryFunction VmaDeviceMemoryCallbacks::pfnFree
    +
    + +

    Optional, can be null.

    + +
    +
    +
    The documentation for this struct was generated from the following file: +
    + + + + diff --git a/docs/html/struct_vma_memory_requirements-members.html b/docs/html/struct_vma_memory_requirements-members.html index 1404da1..aefd96c 100644 --- a/docs/html/struct_vma_memory_requirements-members.html +++ b/docs/html/struct_vma_memory_requirements-members.html @@ -65,9 +65,9 @@ $(function() {

    This is the complete list of members for VmaMemoryRequirements, including all inherited members.

    - - - + + +
    neverAllocateVmaMemoryRequirements
    ownMemoryVmaMemoryRequirements
    preferredFlagsVmaMemoryRequirements
    flagsVmaMemoryRequirements
    preferredFlagsVmaMemoryRequirements
    pUserDataVmaMemoryRequirements
    requiredFlagsVmaMemoryRequirements
    usageVmaMemoryRequirements
    diff --git a/docs/html/struct_vma_memory_requirements.html b/docs/html/struct_vma_memory_requirements.html index b3b2324..c3572f1 100644 --- a/docs/html/struct_vma_memory_requirements.html +++ b/docs/html/struct_vma_memory_requirements.html @@ -70,9 +70,8 @@ $(function() { - - - + + @@ -82,45 +81,23 @@ Public Attributes - - - + + +

    Public Attributes

    VkBool32 ownMemory
     Set to true if this allocation should have its own memory block. More...
     
    VmaMemoryRequirementFlags flags
     
    VmaMemoryUsage usage
     Intended usage of memory. More...
     
    VkMemoryPropertyFlags preferredFlags
     Flags that preferably should be set in a Memory Type chosen for an allocation. More...
     
    VkBool32 neverAllocate
     Set this flag to only try to allocate from existing VkDeviceMemory blocks and never create new such block. More...
     
    void * pUserData
     Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). More...
     

    Member Data Documentation

    - -

    ◆ neverAllocate

    + +

    ◆ flags

    - +
    VkBool32 VmaMemoryRequirements::neverAllocateVmaMemoryRequirementFlags VmaMemoryRequirements::flags
    -

    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.

    -

    It makes no sense to set ownMemory and neverAllocate at the same time.

    - -
    -
    - -

    ◆ ownMemory

    - -
    -
    - - - - -
    VkBool32 VmaMemoryRequirements::ownMemory
    -
    - -

    Set to true if this allocation should have its own memory block.

    -

    Use it for special, big resources, like fullscreen images used as attachments.

    -

    This flag must also be used for host visible resources that you want to map simultaneously because otherwise they might end up as regions of the same VkDeviceMemory, while mapping same VkDeviceMemory multiple times is illegal.

    -
    @@ -138,6 +115,22 @@ Public Attributes

    Flags that preferably should be set in a Memory Type chosen for an allocation.

    Set to 0 if no additional flags are prefered and only requiredFlags should be used. If not 0, it must be a superset or equal to requiredFlags.

    + + + +

    ◆ pUserData

    + +
    +
    + + + + +
    void* VmaMemoryRequirements::pUserData
    +
    + +

    Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().

    +
    diff --git a/docs/html/vk__mem__alloc_8h.html b/docs/html/vk__mem__alloc_8h.html index c0df697..db24e33 100644 --- a/docs/html/vk__mem__alloc_8h.html +++ b/docs/html/vk__mem__alloc_8h.html @@ -74,6 +74,9 @@ $(function() { + + + @@ -84,6 +87,15 @@ Classes + + + + + + + + +

    Classes

    struct  VmaDeviceMemoryCallbacks
     Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory. More...
     
    struct  VmaAllocatorCreateInfo
     Description of a Allocator to be created. More...
     
     
    struct  VmaMemoryRequirements
     
    struct  VmaAllocationInfo
     Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). More...
     
    struct  VmaDefragmentationInfo
     Optional configuration parameters to be passed to function vmaDefragment(). More...
     
    struct  VmaDefragmentationStats
     Statistics returned by function vmaDefragment(). More...
     
    @@ -92,6 +104,20 @@ Macros

    Macros

    + + + + + + + + + + + + + + @@ -99,11 +125,30 @@ Typedefs + + + + + + + + + + + + + +

    Typedefs

    typedef void(VKAPI_PTR * PFN_vmaAllocateDeviceMemoryFunction) (VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
     Callback function called after successful vkAllocateMemory. More...
     
    typedef void(VKAPI_PTR * PFN_vmaFreeDeviceMemoryFunction) (VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
     Callback function called before vkFreeMemory. More...
     
    typedef struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
     Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory. More...
     
    typedef enum VmaAllocatorFlagBits VmaAllocatorFlagBits
     Flags for created VmaAllocator. More...
     
    typedef VkFlags VmaAllocatorFlags
     
    typedef struct VmaAllocatorCreateInfo VmaAllocatorCreateInfo
     Description of a Allocator to be created. More...
     
     
    typedef enum VmaMemoryUsage VmaMemoryUsage
     
    typedef enum VmaMemoryRequirementFlagBits VmaMemoryRequirementFlagBits
     Flags to be passed as VmaMemoryRequirements::flags. More...
     
    typedef VkFlags VmaMemoryRequirementFlags
     
    typedef struct VmaMemoryRequirements VmaMemoryRequirements
     
    typedef struct VmaAllocationInfo VmaAllocationInfo
     Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). More...
     
    typedef struct VmaDefragmentationInfo VmaDefragmentationInfo
     Optional configuration parameters to be passed to function vmaDefragment(). More...
     
    typedef struct VmaDefragmentationStats VmaDefragmentationStats
     Statistics returned by function vmaDefragment(). More...
     
    + + +
    } + + +

    Enumerations

    enum  VmaAllocatorFlagBits { VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, +VMA_ALLOCATOR_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF + }
     Flags for created VmaAllocator. More...
     
    enum  VmaMemoryUsage {
      VMA_MEMORY_USAGE_UNKNOWN = 0, VMA_MEMORY_USAGE_GPU_ONLY = 1, @@ -115,6 +160,13 @@ Enumerations
     
    enum  VmaMemoryRequirementFlagBits { VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT = 0x00000001, +VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT = 0x00000002, +VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT = 0x00000004, +VMA_MEMORY_REQUIREMENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF + }
     Flags to be passed as VmaMemoryRequirements::flags. More...
     
    @@ -141,30 +193,45 @@ Functions - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    Functions

     
    VkResult vmaFindMemoryTypeIndex (VmaAllocator allocator, uint32_t memoryTypeBits, const VmaMemoryRequirements *pMemoryRequirements, uint32_t *pMemoryTypeIndex)
     
    VkResult vmaAllocateMemory (VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
     General purpose memory allocation. More...
     
    VkResult vmaAllocateMemoryForBuffer (VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
     
    VkResult vmaAllocateMemoryForImage (VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
     Function similar to vmaAllocateMemoryForBuffer(). More...
     
    void vmaFreeMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
     Frees memory previously allocated using vmaAllocateMemoryForBuffer() or vmaAllocateMemoryForImage(). More...
     
    VkResult vmaMapMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory, void **ppData)
     
    void vmaUnmapMemory (VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
     
    VkResult vmaCreateBuffer (VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
     
    void vmaDestroyBuffer (VmaAllocator allocator, VkBuffer buffer)
     
    VkResult vmaCreateImage (VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
     Function similar to vmaCreateBuffer(). More...
     
    void vmaDestroyImage (VmaAllocator allocator, VkImage image)
     
    VkResult vmaAllocateMemory (VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
     General purpose memory allocation. More...
     
    VkResult vmaAllocateMemoryForBuffer (VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
     
    VkResult vmaAllocateMemoryForImage (VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
     Function similar to vmaAllocateMemoryForBuffer(). More...
     
    void vmaFreeMemory (VmaAllocator allocator, VmaAllocation allocation)
     Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). More...
     
    void vmaGetAllocationInfo (VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
     Returns current information about specified allocation. More...
     
    void vmaSetAllocationUserData (VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
     Sets pUserData in given allocation to new value. More...
     
    VkResult vmaMapMemory (VmaAllocator allocator, VmaAllocation allocation, void **ppData)
     
    void vmaUnmapMemory (VmaAllocator allocator, VmaAllocation allocation)
     
    void vmaUnmapPersistentlyMappedMemory (VmaAllocator allocator)
     Unmaps persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. More...
     
    VkResult vmaMapPersistentlyMappedMemory (VmaAllocator allocator)
     Maps back persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL. More...
     
    VkResult vmaDefragment (VmaAllocator allocator, VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
     Compacts memory by moving allocations. More...
     
    VkResult vmaCreateBuffer (VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
     
    void vmaDestroyBuffer (VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
     
    VkResult vmaCreateImage (VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
     Function similar to vmaCreateBuffer(). More...
     
    void vmaDestroyImage (VmaAllocator allocator, VkImage image, VmaAllocation allocation)
     
    diff --git a/docs/html/vk__mem__alloc_8h_source.html b/docs/html/vk__mem__alloc_8h_source.html index de40d22..dcd00e3 100644 --- a/docs/html/vk__mem__alloc_8h_source.html +++ b/docs/html/vk__mem__alloc_8h_source.html @@ -62,63 +62,104 @@ $(function() {
    vk_mem_alloc.h
    -Go to the documentation of this file.
    1 //
    2 // Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
    3 //
    4 // Permission is hereby granted, free of charge, to any person obtaining a copy
    5 // of this software and associated documentation files (the "Software"), to deal
    6 // in the Software without restriction, including without limitation the rights
    7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    8 // copies of the Software, and to permit persons to whom the Software is
    9 // furnished to do so, subject to the following conditions:
    10 //
    11 // The above copyright notice and this permission notice shall be included in
    12 // all copies or substantial portions of the Software.
    13 //
    14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20 // THE SOFTWARE.
    21 //
    22 
    23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
    24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
    25 
    155 #include <vulkan/vulkan.h>
    156 
    158 
    162 VK_DEFINE_HANDLE(VmaAllocator)
    163 
    164 typedef struct VmaAllocatorCreateInfo
    166 {
    168 
    169  VkPhysicalDevice physicalDevice;
    171 
    172  VkDevice device;
    174 
    177 
    180 
    181  const VkAllocationCallbacks* pAllocationCallbacks;
    183 
    185 VkResult vmaCreateAllocator(
    186  const VmaAllocatorCreateInfo* pCreateInfo,
    187  VmaAllocator* pAllocator);
    188 
    191  VmaAllocator allocator);
    192 
    198  VmaAllocator allocator,
    199  const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
    200 
    206  VmaAllocator allocator,
    207  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
    208 
    216  VmaAllocator allocator,
    217  uint32_t memoryTypeIndex,
    218  VkMemoryPropertyFlags* pFlags);
    219 
    220 typedef struct VmaStatInfo
    221 {
    222  uint32_t AllocationCount;
    225  VkDeviceSize UsedBytes;
    226  VkDeviceSize UnusedBytes;
    227  VkDeviceSize SuballocationSizeMin, SuballocationSizeAvg, SuballocationSizeMax;
    228  VkDeviceSize UnusedRangeSizeMin, UnusedRangeSizeAvg, UnusedRangeSizeMax;
    229 } VmaStatInfo;
    230 
    232 struct VmaStats
    233 {
    234  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
    235  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
    237 };
    238 
    240 void vmaCalculateStats(
    241  VmaAllocator allocator,
    242  VmaStats* pStats);
    243 
    244 #define VMA_STATS_STRING_ENABLED 1
    245 
    246 #if VMA_STATS_STRING_ENABLED
    247 
    249 
    252  VmaAllocator allocator,
    253  char** ppStatsString,
    254  VkBool32 detailedMap);
    255 
    256 void vmaFreeStatsString(
    257  VmaAllocator allocator,
    258  char* pStatsString);
    259 
    260 #endif // #if VMA_STATS_STRING_ENABLED
    261 
    264 
    269 typedef enum VmaMemoryUsage
    270 {
    283 
    284 typedef struct VmaMemoryRequirements
    285 {
    294  VkBool32 ownMemory;
    303  VkMemoryPropertyFlags requiredFlags;
    308  VkMemoryPropertyFlags preferredFlags;
    315  VkBool32 neverAllocate;
    317 
    332 VkResult vmaFindMemoryTypeIndex(
    333  VmaAllocator allocator,
    334  uint32_t memoryTypeBits,
    335  const VmaMemoryRequirements* pMemoryRequirements,
    336  uint32_t* pMemoryTypeIndex);
    337 
    340 
    357 VkResult vmaAllocateMemory(
    358  VmaAllocator allocator,
    359  const VkMemoryRequirements* pVkMemoryRequirements,
    360  const VmaMemoryRequirements* pVmaMemoryRequirements,
    361  VkMappedMemoryRange* pMemory,
    362  uint32_t* pMemoryTypeIndex);
    363 
    372  VmaAllocator allocator,
    373  VkBuffer buffer,
    374  const VmaMemoryRequirements* pMemoryRequirements,
    375  VkMappedMemoryRange* pMemory,
    376  uint32_t* pMemoryTypeIndex);
    377 
    380  VmaAllocator allocator,
    381  VkImage image,
    382  const VmaMemoryRequirements* pMemoryRequirements,
    383  VkMappedMemoryRange* pMemory,
    384  uint32_t* pMemoryTypeIndex);
    385 
    387 void vmaFreeMemory(
    388  VmaAllocator allocator,
    389  const VkMappedMemoryRange* pMemory);
    390 
    396 VkResult vmaMapMemory(
    397  VmaAllocator allocator,
    398  const VkMappedMemoryRange* pMemory,
    399  void** ppData);
    400 
    401 void vmaUnmapMemory(
    402  VmaAllocator allocator,
    403  const VkMappedMemoryRange* pMemory);
    404 
    407 
    429 VkResult vmaCreateBuffer(
    430  VmaAllocator allocator,
    431  const VkBufferCreateInfo* pCreateInfo,
    432  const VmaMemoryRequirements* pMemoryRequirements,
    433  VkBuffer* pBuffer,
    434  VkMappedMemoryRange* pMemory,
    435  uint32_t* pMemoryTypeIndex);
    436 
    437 void vmaDestroyBuffer(
    438  VmaAllocator allocator,
    439  VkBuffer buffer);
    440 
    442 VkResult vmaCreateImage(
    443  VmaAllocator allocator,
    444  const VkImageCreateInfo* pCreateInfo,
    445  const VmaMemoryRequirements* pMemoryRequirements,
    446  VkImage* pImage,
    447  VkMappedMemoryRange* pMemory,
    448  uint32_t* pMemoryTypeIndex);
    449 
    450 void vmaDestroyImage(
    451  VmaAllocator allocator,
    452  VkImage image);
    453 
    456 #ifdef VMA_IMPLEMENTATION
    457 
    458 #include <cstdlib>
    459 
    460 /*******************************************************************************
    461 CONFIGURATION
    462 
    463 Change these definitions depending on your environment.
    464 */
    465 
    466 #define VMA_USE_STL_CONTAINERS 0
    467 
    468 /* Set this macro to 1 to make the library including and using STL containers:
    469 std::pair, std::vector, std::list, std::unordered_map.
    470 
    471 Set it to 0 or undefined to make the library using its own implementation of
    472 the containers.
    473 */
    474 #if VMA_USE_STL_CONTAINERS
    475 #define VMA_USE_STL_VECTOR 1
    476 #define VMA_USE_STL_UNORDERED_MAP 1
    477 #define VMA_USE_STL_LIST 1
    478 #endif
    479 
    480 #if VMA_USE_STL_VECTOR
    481 #include <vector>
    482 #endif
    483 
    484 #if VMA_USE_STL_UNORDERED_MAP
    485 #include <unordered_map>
    486 #endif
    487 
    488 #if VMA_USE_STL_LIST
    489 #include <list>
    490 #endif
    491 
    492 /*
    493 Following headers are used in this CONFIGURATION section only, so feel free to
    494 remove them if not needed.
    495 */
    496 #include <cassert> // for assert
    497 #include <algorithm> // for min, max
    498 #include <mutex> // for std::mutex
    499 
    500 #ifdef _DEBUG
    501  // Normal assert to check for programmer's errors, especially in Debug configuration.
    502  #define VMA_ASSERT(expr) assert(expr)
    503  // Assert that will be called very often, like inside data structures e.g. operator[].
    504  // Making it non-empty can make program slow.
    505  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
    506 #else
    507  #define VMA_ASSERT(expr)
    508  #define VMA_HEAVY_ASSERT(expr)
    509 #endif
    510 
    511 // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
    512 #define VMA_NULL nullptr
    513 
    514 #define VMA_ALIGN_OF(type) (__alignof(type))
    515 #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
    516 #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
    517 
    518 #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
    519 #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
    520 #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
    521 
    522 #define VMA_DEBUG_LOG(format, ...)
    523 /*
    524 #define VMA_DEBUG_LOG(format, ...) do { \
    525  printf(format, __VA_ARGS__); \
    526  printf("\n"); \
    527 } while(false)
    528 */
    529 
    530 #if VMA_STATS_STRING_ENABLED
    531 
    532 static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
    533 {
    534  _ultoa_s(num, outStr, strLen, 10);
    535 }
    536 static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
    537 {
    538  _ui64toa_s(num, outStr, strLen, 10);
    539 }
    540 
    541 #endif // #if VMA_STATS_STRING_ENABLED
    542 
    543 class VmaMutex
    544 {
    545 public:
    546  VmaMutex() { }
    547  ~VmaMutex() { }
    548  void Lock() { m_Mutex.lock(); }
    549  void Unlock() { m_Mutex.unlock(); }
    550 private:
    551  std::mutex m_Mutex;
    552 };
    553 
    554 /*
    555 Main parameter for function assessing how good is a free suballocation for a new
    556 allocation request.
    557 
    558 - Set to true to use Best-Fit algorithm - prefer smaller blocks, as close to the
    559  size of requested allocations as possible.
    560 - Set to false to use Worst-Fit algorithm - prefer larger blocks, as large as
    561  possible.
    562 
    563 Experiments in special testing environment showed that Best-Fit algorithm is
    564 better.
    565 */
    566 static const bool VMA_BEST_FIT = true;
    567 
    568 /*
    569 Every object will have its own allocation.
    570 Enable for debugging purposes only.
    571 */
    572 static const bool VMA_DEBUG_ALWAYS_OWN_MEMORY = false;
    573 
    574 /*
    575 Minimum alignment of all suballocations, in bytes.
    576 Set to more than 1 for debugging purposes only. Must be power of two.
    577 */
    578 static const VkDeviceSize VMA_DEBUG_ALIGNMENT = 1;
    579 
    580 /*
    581 Minimum margin between suballocations, in bytes.
    582 Set nonzero for debugging purposes only.
    583 */
    584 static const VkDeviceSize VMA_DEBUG_MARGIN = 0;
    585 
    586 /*
    587 Set this to 1 for debugging purposes only, to enable single mutex protecting all
    588 entry calls to the library. Can be useful for debugging multithreading issues.
    589 */
    590 #define VMA_DEBUG_GLOBAL_MUTEX 0
    591 
    592 /*
    593 Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity.
    594 Set to more than 1 for debugging purposes only. Must be power of two.
    595 */
    596 static const VkDeviceSize VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY = 1;
    597 
    598 // Maximum size of a memory heap in Vulkan to consider it "small".
    599 static const VkDeviceSize VMA_SMALL_HEAP_MAX_SIZE = 512 * 1024 * 1024;
    600 // Default size of a block allocated as single VkDeviceMemory from a "large" heap.
    601 static const VkDeviceSize VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE = 256 * 1024 * 1024;
    602 // Default size of a block allocated as single VkDeviceMemory from a "small" heap.
    603 static const VkDeviceSize VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE = 64 * 1024 * 1024;
    604 
    605 /*******************************************************************************
    606 END OF CONFIGURATION
    607 */
    608 
    609 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
    610  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
    611 
    612 // Returns number of bits set to 1 in (v).
    613 static inline uint32_t CountBitsSet(uint32_t v)
    614 {
    615  uint32_t c = v - ((v >> 1) & 0x55555555);
    616  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
    617  c = ((c >> 4) + c) & 0x0F0F0F0F;
    618  c = ((c >> 8) + c) & 0x00FF00FF;
    619  c = ((c >> 16) + c) & 0x0000FFFF;
    620  return c;
    621 }
    622 
    623 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
    624 // Use types like uint32_t, uint64_t as T.
    625 template <typename T>
    626 static inline T VmaAlignUp(T val, T align)
    627 {
    628  return (val + align - 1) / align * align;
    629 }
    630 
    631 // Division with mathematical rounding to nearest number.
    632 template <typename T>
    633 inline T VmaRoundDiv(T x, T y)
    634 {
    635  return (x + (y / (T)2)) / y;
    636 }
    637 /*
    638 Returns true if two memory blocks occupy overlapping pages.
    639 ResourceA must be in less memory offset than ResourceB.
    640 
    641 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
    642 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
    643 */
    644 static inline bool VmaBlocksOnSamePage(
    645  VkDeviceSize resourceAOffset,
    646  VkDeviceSize resourceASize,
    647  VkDeviceSize resourceBOffset,
    648  VkDeviceSize pageSize)
    649 {
    650  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
    651  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
    652  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
    653  VkDeviceSize resourceBStart = resourceBOffset;
    654  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
    655  return resourceAEndPage == resourceBStartPage;
    656 }
    657 
    658 enum VmaSuballocationType
    659 {
    660  VMA_SUBALLOCATION_TYPE_FREE = 0,
    661  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
    662  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
    663  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
    664  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
    665  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
    666  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
    667 };
    668 
    669 /*
    670 Returns true if given suballocation types could conflict and must respect
    671 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
    672 or linear image and another one is optimal image. If type is unknown, behave
    673 conservatively.
    674 */
    675 static inline bool VmaIsBufferImageGranularityConflict(
    676  VmaSuballocationType suballocType1,
    677  VmaSuballocationType suballocType2)
    678 {
    679  if(suballocType1 > suballocType2)
    680  VMA_SWAP(suballocType1, suballocType2);
    681 
    682  switch(suballocType1)
    683  {
    684  case VMA_SUBALLOCATION_TYPE_FREE:
    685  return false;
    686  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
    687  return true;
    688  case VMA_SUBALLOCATION_TYPE_BUFFER:
    689  return
    690  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
    691  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    692  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
    693  return
    694  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
    695  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
    696  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    697  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
    698  return
    699  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    700  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
    701  return false;
    702  default:
    703  VMA_ASSERT(0);
    704  return true;
    705  }
    706 }
    707 
    708 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
    709 struct VmaMutexLock
    710 {
    711 public:
    712  VmaMutexLock(VmaMutex& mutex) : m_Mutex(mutex) { mutex.Lock(); }
    713  ~VmaMutexLock() { m_Mutex.Unlock(); }
    714 
    715 private:
    716  VmaMutex& m_Mutex;
    717 };
    718 
    719 #if VMA_DEBUG_GLOBAL_MUTEX
    720  static VmaMutex gDebugGlobalMutex;
    721  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex);
    722 #else
    723  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
    724 #endif
    725 
    726 // Minimum size of a free suballocation to register it in the free suballocation collection.
    727 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
    728 
    729 /*
    730 Performs binary search and returns iterator to first element that is greater or
    731 equal to (key), according to comparison (cmp).
    732 
    733 Cmp should return true if first argument is less than second argument.
    734 
    735 Returned value is the found element, if present in the collection or place where
    736 new element with value (key) should be inserted.
    737 */
    738 template <typename IterT, typename KeyT, typename CmpT>
    739 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)
    740 {
    741  size_t down = 0, up = (end - beg);
    742  while(down < up)
    743  {
    744  const size_t mid = (down + up) / 2;
    745  if(cmp(*(beg+mid), key))
    746  down = mid + 1;
    747  else
    748  up = mid;
    749  }
    750  return beg + down;
    751 }
    752 
    754 // Memory allocation
    755 
    756 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
    757 {
    758  if((pAllocationCallbacks != VMA_NULL) &&
    759  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
    760  {
    761  return (*pAllocationCallbacks->pfnAllocation)(
    762  pAllocationCallbacks->pUserData,
    763  size,
    764  alignment,
    765  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
    766  }
    767  else
    768  {
    769  return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
    770  }
    771 }
    772 
    773 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
    774 {
    775  if((pAllocationCallbacks != VMA_NULL) &&
    776  (pAllocationCallbacks->pfnFree != VMA_NULL))
    777  {
    778  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
    779  }
    780  else
    781  {
    782  VMA_SYSTEM_FREE(ptr);
    783  }
    784 }
    785 
    786 template<typename T>
    787 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
    788 {
    789  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
    790 }
    791 
    792 template<typename T>
    793 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
    794 {
    795  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
    796 }
    797 
    798 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
    799 
    800 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
    801 
    802 template<typename T>
    803 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
    804 {
    805  ptr->~T();
    806  VmaFree(pAllocationCallbacks, ptr);
    807 }
    808 
    809 template<typename T>
    810 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
    811 {
    812  if(ptr != VMA_NULL)
    813  {
    814  for(size_t i = count; i--; )
    815  ptr[i].~T();
    816  VmaFree(pAllocationCallbacks, ptr);
    817  }
    818 }
    819 
    820 // STL-compatible allocator.
    821 template<typename T>
    822 class VmaStlAllocator
    823 {
    824 public:
    825  const VkAllocationCallbacks* const m_pCallbacks;
    826  typedef T value_type;
    827 
    828  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
    829  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
    830 
    831  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
    832  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
    833 
    834  template<typename U>
    835  bool operator==(const VmaStlAllocator<U>& rhs) const
    836  {
    837  return m_pCallbacks == rhs.m_pCallbacks;
    838  }
    839  template<typename U>
    840  bool operator!=(const VmaStlAllocator<U>& rhs) const
    841  {
    842  return m_pCallbacks != rhs.m_pCallbacks;
    843  }
    844 };
    845 
    846 #if VMA_USE_STL_VECTOR
    847 
    848 #define VmaVector std::vector
    849 
    850 template<typename T, typename allocatorT>
    851 static void VectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
    852 {
    853  vec.insert(vec.begin() + index, item);
    854 }
    855 
    856 template<typename T, typename allocatorT>
    857 static void VectorRemove(std::vector<T, allocatorT>& vec, size_t index)
    858 {
    859  vec.erase(vec.begin() + index);
    860 }
    861 
    862 #else // #if VMA_USE_STL_VECTOR
    863 
    864 /* Class with interface compatible with subset of std::vector.
    865 T must be POD because constructors and destructors are not called and memcpy is
    866 used for these objects. */
    867 template<typename T, typename AllocatorT>
    868 class VmaVector
    869 {
    870 public:
    871  VmaVector(AllocatorT& allocator) :
    872  m_Allocator(allocator),
    873  m_pArray(VMA_NULL),
    874  m_Count(0),
    875  m_Capacity(0)
    876  {
    877  }
    878 
    879  VmaVector(size_t count, AllocatorT& allocator) :
    880  m_Allocator(allocator),
    881  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator->m_pCallbacks, count) : VMA_NULL),
    882  m_Count(count),
    883  m_Capacity(count)
    884  {
    885  }
    886 
    887  VmaVector(const VmaVector<T, AllocatorT>& src) :
    888  m_Allocator(src.m_Allocator),
    889  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(allocator->m_pCallbacks, src.m_Count) : VMA_NULL),
    890  m_Count(src.m_Count),
    891  m_Capacity(src.m_Count)
    892  {
    893  if(m_Count != 0)
    894  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
    895  }
    896 
    897  ~VmaVector()
    898  {
    899  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    900  }
    901 
    902  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
    903  {
    904  if(&rhs != this)
    905  {
    906  Resize(rhs.m_Count);
    907  if(m_Count != 0)
    908  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
    909  }
    910  return *this;
    911  }
    912 
    913  bool empty() const { return m_Count == 0; }
    914  size_t size() const { return m_Count; }
    915  T* data() { return m_pArray; }
    916  const T* data() const { return m_pArray; }
    917 
    918  T& operator[](size_t index)
    919  {
    920  VMA_HEAVY_ASSERT(index < m_Count);
    921  return m_pArray[index];
    922  }
    923  const T& operator[](size_t index) const
    924  {
    925  VMA_HEAVY_ASSERT(index < m_Count);
    926  return m_pArray[index];
    927  }
    928 
    929  T& front()
    930  {
    931  VMA_HEAVY_ASSERT(m_Count > 0);
    932  return m_pArray[0];
    933  }
    934  const T& front() const
    935  {
    936  VMA_HEAVY_ASSERT(m_Count > 0);
    937  return m_pArray[0];
    938  }
    939  T& back()
    940  {
    941  VMA_HEAVY_ASSERT(m_Count > 0);
    942  return m_pArray[m_Count - 1];
    943  }
    944  const T& back() const
    945  {
    946  VMA_HEAVY_ASSERT(m_Count > 0);
    947  return m_pArray[m_Count - 1];
    948  }
    949 
    950  void reserve(size_t newCapacity, bool freeMemory = false)
    951  {
    952  newCapacity = VMA_MAX(newCapacity, m_Count);
    953 
    954  if((newCapacity < m_Capacity) && !freeMemory)
    955  newCapacity = m_Capacity;
    956 
    957  if(newCapacity != m_Capacity)
    958  {
    959  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_hAllocator, newCapacity) : VMA_NULL;
    960  if(m_Count != 0)
    961  memcpy(newArray, m_pArray, m_Count * sizeof(T));
    962  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    963  m_Capacity = newCapacity;
    964  m_pArray = newArray;
    965  }
    966  }
    967 
    968  void resize(size_t newCount, bool freeMemory = false)
    969  {
    970  size_t newCapacity = m_Capacity;
    971  if(newCount > m_Capacity)
    972  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
    973  else if(freeMemory)
    974  newCapacity = newCount;
    975 
    976  if(newCapacity != m_Capacity)
    977  {
    978  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
    979  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
    980  if(elementsToCopy != 0)
    981  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
    982  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    983  m_Capacity = newCapacity;
    984  m_pArray = newArray;
    985  }
    986 
    987  m_Count = newCount;
    988  }
    989 
    990  void clear(bool freeMemory = false)
    991  {
    992  resize(0, freeMemory);
    993  }
    994 
    995  void insert(size_t index, const T& src)
    996  {
    997  VMA_HEAVY_ASSERT(index <= m_Count);
    998  const size_t oldCount = size();
    999  resize(oldCount + 1);
    1000  if(index < oldCount)
    1001  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
    1002  m_pArray[index] = src;
    1003  }
    1004 
    1005  void remove(size_t index)
    1006  {
    1007  VMA_HEAVY_ASSERT(index < m_Count);
    1008  const size_t oldCount = size();
    1009  if(index < oldCount - 1)
    1010  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
    1011  resize(oldCount - 1);
    1012  }
    1013 
    1014  void push_back(const T& src)
    1015  {
    1016  const size_t newIndex = size();
    1017  resize(newIndex + 1);
    1018  m_pArray[newIndex] = src;
    1019  }
    1020 
    1021  void pop_back()
    1022  {
    1023  VMA_HEAVY_ASSERT(m_Count > 0);
    1024  resize(size() - 1);
    1025  }
    1026 
    1027  void push_front(const T& src)
    1028  {
    1029  insert(0, src);
    1030  }
    1031 
    1032  void pop_front()
    1033  {
    1034  VMA_HEAVY_ASSERT(m_Count > 0);
    1035  remove(0);
    1036  }
    1037 
    1038  typedef T* iterator;
    1039 
    1040  iterator begin() { return m_pArray; }
    1041  iterator end() { return m_pArray + m_Count; }
    1042 
    1043 private:
    1044  AllocatorT m_Allocator;
    1045  T* m_pArray;
    1046  size_t m_Count;
    1047  size_t m_Capacity;
    1048 };
    1049 
    1050 template<typename T, typename allocatorT>
    1051 static void VectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
    1052 {
    1053  vec.insert(index, item);
    1054 }
    1055 
    1056 template<typename T, typename allocatorT>
    1057 static void VectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
    1058 {
    1059  vec.remove(index);
    1060 }
    1061 
    1062 #endif // #if VMA_USE_STL_VECTOR
    1063 
    1065 // class VmaPoolAllocator
    1066 
    1067 /*
    1068 Allocator for objects of type T using a list of arrays (pools) to speed up
    1069 allocation. Number of elements that can be allocated is not bounded because
    1070 allocator can create multiple blocks.
    1071 */
    1072 template<typename T>
    1073 class VmaPoolAllocator
    1074 {
    1075 public:
    1076  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
    1077  ~VmaPoolAllocator();
    1078  void Clear();
    1079  T* Alloc();
    1080  void Free(T* ptr);
    1081 
    1082 private:
    1083  union Item
    1084  {
    1085  uint32_t NextFreeIndex;
    1086  T Value;
    1087  };
    1088 
    1089  struct ItemBlock
    1090  {
    1091  Item* pItems;
    1092  uint32_t FirstFreeIndex;
    1093  };
    1094 
    1095  const VkAllocationCallbacks* m_pAllocationCallbacks;
    1096  size_t m_ItemsPerBlock;
    1097  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
    1098 
    1099  ItemBlock& CreateNewBlock();
    1100 };
    1101 
    1102 template<typename T>
    1103 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
    1104  m_pAllocationCallbacks(pAllocationCallbacks),
    1105  m_ItemsPerBlock(itemsPerBlock),
    1106  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
    1107 {
    1108  VMA_ASSERT(itemsPerBlock > 0);
    1109 }
    1110 
    1111 template<typename T>
    1112 VmaPoolAllocator<T>::~VmaPoolAllocator()
    1113 {
    1114  Clear();
    1115 }
    1116 
    1117 template<typename T>
    1118 void VmaPoolAllocator<T>::Clear()
    1119 {
    1120  for(size_t i = m_ItemBlocks.size(); i--; )
    1121  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
    1122  m_ItemBlocks.clear();
    1123 }
    1124 
    1125 template<typename T>
    1126 T* VmaPoolAllocator<T>::Alloc()
    1127 {
    1128  for(size_t i = m_ItemBlocks.size(); i--; )
    1129  {
    1130  ItemBlock& block = m_ItemBlocks[i];
    1131  // This block has some free items: Use first one.
    1132  if(block.FirstFreeIndex != UINT_MAX)
    1133  {
    1134  Item* const pItem = &block.pItems[block.FirstFreeIndex];
    1135  block.FirstFreeIndex = pItem->NextFreeIndex;
    1136  return &pItem->Value;
    1137  }
    1138  }
    1139 
    1140  // No block has free item: Create new one and use it.
    1141  ItemBlock& newBlock = CreateNewBlock();
    1142  Item* const pItem = &newBlock.pItems[0];
    1143  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
    1144  return &pItem->Value;
    1145 }
    1146 
    1147 template<typename T>
    1148 void VmaPoolAllocator<T>::Free(T* ptr)
    1149 {
    1150  // Search all memory blocks to find ptr.
    1151  for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
    1152  {
    1153  ItemBlock& block = m_ItemBlocks[i];
    1154 
    1155  // Casting to union.
    1156  Item* pItemPtr;
    1157  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
    1158 
    1159  // Check if pItemPtr is in address range of this block.
    1160  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
    1161  {
    1162  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
    1163  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
    1164  block.FirstFreeIndex = index;
    1165  return;
    1166  }
    1167  }
    1168  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
    1169 }
    1170 
    1171 template<typename T>
    1172 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
    1173 {
    1174  ItemBlock newBlock = {
    1175  vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
    1176 
    1177  m_ItemBlocks.push_back(newBlock);
    1178 
    1179  // Setup singly-linked list of all free items in this block.
    1180  for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
    1181  newBlock.pItems[i].NextFreeIndex = i + 1;
    1182  newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT_MAX;
    1183  return m_ItemBlocks.back();
    1184 }
    1185 
    1187 // class VmaRawList, VmaList
    1188 
    1189 #if VMA_USE_STL_LIST
    1190 
    1191 #define VmaList std::list
    1192 
    1193 #else // #if VMA_USE_STL_LIST
    1194 
    1195 template<typename T>
    1196 struct VmaListItem
    1197 {
    1198  VmaListItem* pPrev;
    1199  VmaListItem* pNext;
    1200  T Value;
    1201 };
    1202 
    1203 // Doubly linked list.
    1204 template<typename T>
    1205 class VmaRawList
    1206 {
    1207 public:
    1208  typedef VmaListItem<T> ItemType;
    1209 
    1210  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
    1211  ~VmaRawList();
    1212  void Clear();
    1213 
    1214  size_t GetCount() const { return m_Count; }
    1215  bool IsEmpty() const { return m_Count == 0; }
    1216 
    1217  ItemType* Front() { return m_pFront; }
    1218  const ItemType* Front() const { return m_pFront; }
    1219  ItemType* Back() { return m_pBack; }
    1220  const ItemType* Back() const { return m_pBack; }
    1221 
    1222  ItemType* PushBack();
    1223  ItemType* PushFront();
    1224  ItemType* PushBack(const T& value);
    1225  ItemType* PushFront(const T& value);
    1226  void PopBack();
    1227  void PopFront();
    1228 
    1229  // Item can be null - it means PushBack.
    1230  ItemType* InsertBefore(ItemType* pItem);
    1231  // Item can be null - it means PushFront.
    1232  ItemType* InsertAfter(ItemType* pItem);
    1233 
    1234  ItemType* InsertBefore(ItemType* pItem, const T& value);
    1235  ItemType* InsertAfter(ItemType* pItem, const T& value);
    1236 
    1237  void Remove(ItemType* pItem);
    1238 
    1239 private:
    1240  const VkAllocationCallbacks* const m_pAllocationCallbacks;
    1241  VmaPoolAllocator<ItemType> m_ItemAllocator;
    1242  ItemType* m_pFront;
    1243  ItemType* m_pBack;
    1244  size_t m_Count;
    1245 
    1246  // Declared not defined, to block copy constructor and assignment operator.
    1247  VmaRawList(const VmaRawList<T>& src);
    1248  VmaRawList<T>& operator=(const VmaRawList<T>& rhs);
    1249 };
    1250 
    1251 template<typename T>
    1252 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
    1253  m_pAllocationCallbacks(pAllocationCallbacks),
    1254  m_ItemAllocator(pAllocationCallbacks, 128),
    1255  m_pFront(VMA_NULL),
    1256  m_pBack(VMA_NULL),
    1257  m_Count(0)
    1258 {
    1259 }
    1260 
    1261 template<typename T>
    1262 VmaRawList<T>::~VmaRawList()
    1263 {
    1264  // Intentionally not calling Clear, because that would be unnecessary
    1265  // computations to return all items to m_ItemAllocator as free.
    1266 }
    1267 
    1268 template<typename T>
    1269 void VmaRawList<T>::Clear()
    1270 {
    1271  if(IsEmpty() == false)
    1272  {
    1273  ItemType* pItem = m_pBack;
    1274  while(pItem != VMA_NULL)
    1275  {
    1276  ItemType* const pPrevItem = pItem->pPrev;
    1277  m_ItemAllocator.Free(pItem);
    1278  pItem = pPrevItem;
    1279  }
    1280  m_pFront = VMA_NULL;
    1281  m_pBack = VMA_NULL;
    1282  m_Count = 0;
    1283  }
    1284 }
    1285 
    1286 template<typename T>
    1287 VmaListItem<T>* VmaRawList<T>::PushBack()
    1288 {
    1289  ItemType* const pNewItem = m_ItemAllocator.Alloc();
    1290  pNewItem->pNext = VMA_NULL;
    1291  if(IsEmpty())
    1292  {
    1293  pNewItem->pPrev = VMA_NULL;
    1294  m_pFront = pNewItem;
    1295  m_pBack = pNewItem;
    1296  m_Count = 1;
    1297  }
    1298  else
    1299  {
    1300  pNewItem->pPrev = m_pBack;
    1301  m_pBack->pNext = pNewItem;
    1302  m_pBack = pNewItem;
    1303  ++m_Count;
    1304  }
    1305  return pNewItem;
    1306 }
    1307 
    1308 template<typename T>
    1309 VmaListItem<T>* VmaRawList<T>::PushFront()
    1310 {
    1311  ItemType* const pNewItem = m_ItemAllocator.Alloc();
    1312  pNewItem->pPrev = VMA_NULL;
    1313  if(IsEmpty())
    1314  {
    1315  pNewItem->pNext = VMA_NULL;
    1316  m_pFront = pNewItem;
    1317  m_pBack = pNewItem;
    1318  m_Count = 1;
    1319  }
    1320  else
    1321  {
    1322  pNewItem->pNext = m_pFront;
    1323  m_pFront->pPrev = pNewItem;
    1324  m_pFront = pNewItem;
    1325  ++m_Count;
    1326  }
    1327  return pNewItem;
    1328 }
    1329 
    1330 template<typename T>
    1331 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
    1332 {
    1333  ItemType* const pNewItem = PushBack();
    1334  pNewItem->Value = value;
    1335  return pNewItem;
    1336 }
    1337 
    1338 template<typename T>
    1339 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
    1340 {
    1341  ItemType* const pNewItem = PushFront();
    1342  pNewItem->Value = value;
    1343  return newItem;
    1344 }
    1345 
    1346 template<typename T>
    1347 void VmaRawList<T>::PopBack()
    1348 {
    1349  VMA_HEAVY_ASSERT(m_Count > 0);
    1350  ItemType* const pBackItem = m_pBack;
    1351  ItemType* const pPrevItem = pBackItem->pPrev;
    1352  if(pPrevItem != VMA_NULL)
    1353  pPrevItem->pNext = VMA_NULL;
    1354  m_pBack = pPrevItem;
    1355  m_ItemAllocator.Free(pBackItem);
    1356  --m_Count;
    1357 }
    1358 
    1359 template<typename T>
    1360 void VmaRawList<T>::PopFront()
    1361 {
    1362  VMA_HEAVY_ASSERT(m_Count > 0);
    1363  ItemType* const pFrontItem = m_pFront;
    1364  ItemType* const pNextItem = pFrontItem->pNext;
    1365  if(pNextItem != VMA_NULL)
    1366  pNextItem->pPrev = VMA_NULL;
    1367  m_pFront = pNextItem;
    1368  m_ItemAllocator.Free(pFrontItem);
    1369  --m_Count;
    1370 }
    1371 
    1372 template<typename T>
    1373 void VmaRawList<T>::Remove(ItemType* pItem)
    1374 {
    1375  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
    1376  VMA_HEAVY_ASSERT(m_Count > 0);
    1377 
    1378  if(pItem->pPrev != VMA_NULL)
    1379  pItem->pPrev->pNext = pItem->pNext;
    1380  else
    1381  {
    1382  VMA_HEAVY_ASSERT(m_pFront == pItem);
    1383  m_pFront = pItem->pNext;
    1384  }
    1385 
    1386  if(pItem->pNext != VMA_NULL)
    1387  pItem->pNext->pPrev = pItem->pPrev;
    1388  else
    1389  {
    1390  VMA_HEAVY_ASSERT(m_pBack == pItem);
    1391  m_pBack = pItem->pPrev;
    1392  }
    1393 
    1394  m_ItemAllocator.Free(pItem);
    1395  --m_Count;
    1396 }
    1397 
    1398 template<typename T>
    1399 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
    1400 {
    1401  if(pItem != VMA_NULL)
    1402  {
    1403  ItemType* const prevItem = pItem->pPrev;
    1404  ItemType* const newItem = m_ItemAllocator.Alloc();
    1405  newItem->pPrev = prevItem;
    1406  newItem->pNext = pItem;
    1407  pItem->pPrev = newItem;
    1408  if(prevItem != VMA_NULL)
    1409  prevItem->pNext = newItem;
    1410  else
    1411  {
    1412  VMA_HEAVY_ASSERT(m_pFront = pItem);
    1413  m_pFront = newItem;
    1414  }
    1415  ++m_Count;
    1416  return newItem;
    1417  }
    1418  else
    1419  return PushBack();
    1420 }
    1421 
    1422 template<typename T>
    1423 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
    1424 {
    1425  if(pItem != VMA_NULL)
    1426  {
    1427  ItemType* const nextItem = pItem->pNext;
    1428  ItemType* const newItem = m_ItemAllocator.Alloc();
    1429  newItem->pNext = nextItem;
    1430  newItem->pPrev = pItem;
    1431  pItem->pNext = newItem;
    1432  if(nextItem != VMA_NULL)
    1433  nextItem->pPrev = newItem;
    1434  else
    1435  {
    1436  VMA_HEAVY_ASSERT(m_pBack = pItem);
    1437  m_pBack = newItem;
    1438  }
    1439  ++m_Count;
    1440  return newItem;
    1441  }
    1442  else
    1443  return PushFront();
    1444 }
    1445 
    1446 template<typename T>
    1447 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
    1448 {
    1449  ItemType* const newItem = InsertBefore(pItem);
    1450  newItem->Value = value;
    1451  return newItem;
    1452 }
    1453 
    1454 template<typename T>
    1455 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
    1456 {
    1457  ItemType* const newItem = InsertAfter(pItem);
    1458  newItem->Value = value;
    1459  return newItem;
    1460 }
    1461 
    1462 template<typename T, typename AllocatorT>
    1463 class VmaList
    1464 {
    1465 public:
    1466  class iterator
    1467  {
    1468  public:
    1469  iterator() :
    1470  m_pList(VMA_NULL),
    1471  m_pItem(VMA_NULL)
    1472  {
    1473  }
    1474 
    1475  T& operator*() const
    1476  {
    1477  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1478  return m_pItem->Value;
    1479  }
    1480  T* operator->() const
    1481  {
    1482  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1483  return &m_pItem->Value;
    1484  }
    1485 
    1486  iterator& operator++()
    1487  {
    1488  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1489  m_pItem = m_pItem->pNext;
    1490  return *this;
    1491  }
    1492  iterator& operator--()
    1493  {
    1494  if(m_pItem != VMA_NULL)
    1495  m_pItem = m_pItem->pPrev;
    1496  else
    1497  {
    1498  VMA_HEAVY_ASSERT(!m_pList.IsEmpty());
    1499  m_pItem = m_pList->Back();
    1500  }
    1501  return *this;
    1502  }
    1503 
    1504  iterator operator++(int)
    1505  {
    1506  iterator result = *this;
    1507  ++*this;
    1508  return result;
    1509  }
    1510  iterator operator--(int)
    1511  {
    1512  iterator result = *this;
    1513  --*this;
    1514  return result;
    1515  }
    1516 
    1517  bool operator==(const iterator& rhs) const
    1518  {
    1519  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1520  return m_pItem == rhs.m_pItem;
    1521  }
    1522  bool operator!=(const iterator& rhs) const
    1523  {
    1524  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1525  return m_pItem != rhs.m_pItem;
    1526  }
    1527 
    1528  private:
    1529  VmaRawList<T>* m_pList;
    1530  VmaListItem<T>* m_pItem;
    1531 
    1532  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
    1533  m_pList(pList),
    1534  m_pItem(pItem)
    1535  {
    1536  }
    1537 
    1538  friend class VmaList<T, AllocatorT>;
    1539  friend class VmaList<T, AllocatorT>:: const_iterator;
    1540  };
    1541 
    1542  class const_iterator
    1543  {
    1544  public:
    1545  const_iterator() :
    1546  m_pList(VMA_NULL),
    1547  m_pItem(VMA_NULL)
    1548  {
    1549  }
    1550 
    1551  const_iterator(const iterator& src) :
    1552  m_pList(src.m_pList),
    1553  m_pItem(src.m_pItem)
    1554  {
    1555  }
    1556 
    1557  const T& operator*() const
    1558  {
    1559  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1560  return m_pItem->Value;
    1561  }
    1562  const T* operator->() const
    1563  {
    1564  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1565  return &m_pItem->Value;
    1566  }
    1567 
    1568  const_iterator& operator++()
    1569  {
    1570  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1571  m_pItem = m_pItem->pNext;
    1572  return *this;
    1573  }
    1574  const_iterator& operator--()
    1575  {
    1576  if(m_pItem != VMA_NULL)
    1577  m_pItem = m_pItem->pPrev;
    1578  else
    1579  {
    1580  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
    1581  m_pItem = m_pList->Back();
    1582  }
    1583  return *this;
    1584  }
    1585 
    1586  const_iterator operator++(int)
    1587  {
    1588  const_iterator result = *this;
    1589  ++*this;
    1590  return result;
    1591  }
    1592  const_iterator operator--(int)
    1593  {
    1594  const_iterator result = *this;
    1595  --*this;
    1596  return result;
    1597  }
    1598 
    1599  bool operator==(const const_iterator& rhs) const
    1600  {
    1601  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1602  return m_pItem == rhs.m_pItem;
    1603  }
    1604  bool operator!=(const const_iterator& rhs) const
    1605  {
    1606  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1607  return m_pItem != rhs.m_pItem;
    1608  }
    1609 
    1610  private:
    1611  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
    1612  m_pList(pList),
    1613  m_pItem(pItem)
    1614  {
    1615  }
    1616 
    1617  const VmaRawList<T>* m_pList;
    1618  const VmaListItem<T>* m_pItem;
    1619 
    1620  friend class VmaList<T, AllocatorT>;
    1621  };
    1622 
    1623  VmaList(AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
    1624 
    1625  bool empty() const { return m_RawList.IsEmpty(); }
    1626  size_t size() const { return m_RawList.GetCount(); }
    1627 
    1628  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
    1629  iterator end() { return iterator(&m_RawList, VMA_NULL); }
    1630 
    1631  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
    1632  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
    1633 
    1634  void clear() { m_RawList.Clear(); }
    1635  void push_back(const T& value) { m_RawList.PushBack(value); }
    1636  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
    1637  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
    1638 
    1639 private:
    1640  VmaRawList<T> m_RawList;
    1641 };
    1642 
    1643 #endif // #if VMA_USE_STL_LIST
    1644 
    1646 // class VmaMap
    1647 
    1648 #if VMA_USE_STL_UNORDERED_MAP
    1649 
    1650 #define VmaPair std::pair
    1651 
    1652 #define VMA_MAP_TYPE(KeyT, ValueT) \
    1653  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
    1654 
    1655 #else // #if VMA_USE_STL_UNORDERED_MAP
    1656 
    1657 template<typename T1, typename T2>
    1658 struct VmaPair
    1659 {
    1660  T1 first;
    1661  T2 second;
    1662 
    1663  VmaPair() : first(), second() { }
    1664  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
    1665 };
    1666 
    1667 /* Class compatible with subset of interface of std::unordered_map.
    1668 KeyT, ValueT must be POD because they will be stored in VmaVector.
    1669 */
    1670 template<typename KeyT, typename ValueT>
    1671 class VmaMap
    1672 {
    1673 public:
    1674  typedef VmaPair<KeyT, ValueT> PairType;
    1675  typedef PairType* iterator;
    1676 
    1677  VmaMap(VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
    1678 
    1679  iterator begin() { return m_Vector.begin(); }
    1680  iterator end() { return m_Vector.end(); }
    1681 
    1682  void insert(const PairType& pair);
    1683  iterator find(const KeyT& key);
    1684  void erase(iterator it);
    1685 
    1686 private:
    1687  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
    1688 };
    1689 
    1690 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
    1691 
    1692 template<typename FirstT, typename SecondT>
    1693 struct VmaPairFirstLess
    1694 {
    1695  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
    1696  {
    1697  return lhs.first < rhs.first;
    1698  }
    1699  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
    1700  {
    1701  return lhs.first < rhsFirst;
    1702  }
    1703 };
    1704 
    1705 template<typename KeyT, typename ValueT>
    1706 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
    1707 {
    1708  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
    1709  m_Vector.data(),
    1710  m_Vector.data() + m_Vector.size(),
    1711  pair,
    1712  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
    1713  VectorInsert(m_Vector, indexToInsert, pair);
    1714 }
    1715 
    1716 template<typename KeyT, typename ValueT>
    1717 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
    1718 {
    1719  PairType* it = VmaBinaryFindFirstNotLess(
    1720  m_Vector.data(),
    1721  m_Vector.data() + m_Vector.size(),
    1722  key,
    1723  VmaPairFirstLess<KeyT, ValueT>());
    1724  if((it != m_Vector.end()) && (it->first == key))
    1725  return it;
    1726  else
    1727  return m_Vector.end();
    1728 }
    1729 
    1730 template<typename KeyT, typename ValueT>
    1731 void VmaMap<KeyT, ValueT>::erase(iterator it)
    1732 {
    1733  VectorRemove(m_Vector, it - m_Vector.begin());
    1734 }
    1735 
    1736 #endif // #if VMA_USE_STL_UNORDERED_MAP
    1737 
    1738 /*
    1739 Represents a region of VmaAllocation that is either assigned and returned as
    1740 allocated memory block or free.
    1741 */
    1742 struct VmaSuballocation
    1743 {
    1744  VkDeviceSize offset;
    1745  VkDeviceSize size;
    1746  VmaSuballocationType type;
    1747 };
    1748 
    1749 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
    1750 
    1751 // Parameters of an allocation.
    1752 struct VmaAllocationRequest
    1753 {
    1754  VmaSuballocationList::iterator freeSuballocationItem;
    1755  VkDeviceSize offset;
    1756 };
    1757 
    1758 /* Single block of memory - VkDeviceMemory with all the data about its regions
    1759 assigned or free. */
    1760 class VmaAllocation
    1761 {
    1762 public:
    1763  VkDeviceMemory m_hMemory;
    1764  VkDeviceSize m_Size;
    1765  uint32_t m_FreeCount;
    1766  VkDeviceSize m_SumFreeSize;
    1767  VmaSuballocationList m_Suballocations;
    1768  // Suballocations that are free and have size greater than certain threshold.
    1769  // Sorted by size, ascending.
    1770  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
    1771 
    1772  VmaAllocation(VmaAllocator hAllocator);
    1773 
    1774  ~VmaAllocation()
    1775  {
    1776  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
    1777  }
    1778 
    1779  // Always call after construction.
    1780  void Init(VkDeviceMemory newMemory, VkDeviceSize newSize);
    1781  // Always call before destruction.
    1782  void Destroy(VmaAllocator allocator);
    1783 
    1784  // Validates all data structures inside this object. If not valid, returns false.
    1785  bool Validate() const;
    1786 
    1787  // Tries to find a place for suballocation with given parameters inside this allocation.
    1788  // If succeeded, fills pAllocationRequest and returns true.
    1789  // If failed, returns false.
    1790  bool CreateAllocationRequest(
    1791  VkDeviceSize bufferImageGranularity,
    1792  VkDeviceSize allocSize,
    1793  VkDeviceSize allocAlignment,
    1794  VmaSuballocationType allocType,
    1795  VmaAllocationRequest* pAllocationRequest);
    1796 
    1797  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
    1798  // If yes, fills pOffset and returns true. If no, returns false.
    1799  bool CheckAllocation(
    1800  VkDeviceSize bufferImageGranularity,
    1801  VkDeviceSize allocSize,
    1802  VkDeviceSize allocAlignment,
    1803  VmaSuballocationType allocType,
    1804  VmaSuballocationList::const_iterator freeSuballocItem,
    1805  VkDeviceSize* pOffset) const;
    1806 
    1807  // Returns true if this allocation is empty - contains only single free suballocation.
    1808  bool IsEmpty() const;
    1809 
    1810  // Makes actual allocation based on request. Request must already be checked
    1811  // and valid.
    1812  void Alloc(
    1813  const VmaAllocationRequest& request,
    1814  VmaSuballocationType type,
    1815  VkDeviceSize allocSize);
    1816 
    1817  // Frees suballocation assigned to given memory region.
    1818  void Free(const VkMappedMemoryRange* pMemory);
    1819 
    1820 #if VMA_STATS_STRING_ENABLED
    1821  void PrintDetailedMap(class VmaStringBuilder& sb) const;
    1822 #endif
    1823 
    1824 private:
    1825  // Given free suballocation, it merges it with following one, which must also be free.
    1826  void MergeFreeWithNext(VmaSuballocationList::iterator item);
    1827  // Releases given suballocation, making it free. Merges it with adjacent free
    1828  // suballocations if applicable.
    1829  void FreeSuballocation(VmaSuballocationList::iterator suballocItem);
    1830  // Given free suballocation, it inserts it into sorted list of
    1831  // m_FreeSuballocationsBySize if it's suitable.
    1832  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
    1833  // Given free suballocation, it removes it from sorted list of
    1834  // m_FreeSuballocationsBySize if it's suitable.
    1835  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
    1836 };
    1837 
    1838 // Allocation for an object that has its own private VkDeviceMemory.
    1839 struct VmaOwnAllocation
    1840 {
    1841  VkDeviceMemory m_hMemory;
    1842  VkDeviceSize m_Size;
    1843  VmaSuballocationType m_Type;
    1844 };
    1845 
    1846 struct VmaOwnAllocationMemoryHandleLess
    1847 {
    1848  bool operator()(const VmaOwnAllocation& lhs, const VmaOwnAllocation& rhs) const
    1849  {
    1850  return lhs.m_hMemory < rhs.m_hMemory;
    1851  }
    1852  bool operator()(const VmaOwnAllocation& lhs, VkDeviceMemory rhsMem) const
    1853  {
    1854  return lhs.m_hMemory < rhsMem;
    1855  }
    1856 };
    1857 
    1858 /* Sequence of VmaAllocation. Represents memory blocks allocated for a specific
    1859 Vulkan memory type. */
    1860 struct VmaAllocationVector
    1861 {
    1862  // Incrementally sorted by sumFreeSize, ascending.
    1863  VmaVector< VmaAllocation*, VmaStlAllocator<VmaAllocation*> > m_Allocations;
    1864 
    1865  VmaAllocationVector(VmaAllocator hAllocator);
    1866  ~VmaAllocationVector();
    1867 
    1868  bool IsEmpty() const { return m_Allocations.empty(); }
    1869 
    1870  // Tries to free memory from any if its Allocations.
    1871  // Returns index of Allocation that the memory was freed from, or -1 if not found.
    1872  size_t Free(const VkMappedMemoryRange* pMemory);
    1873 
    1874  // Performs single step in sorting m_Allocations. They may not be fully sorted
    1875  // after this call.
    1876  void IncrementallySortAllocations();
    1877 
    1878  // Adds statistics of this AllocationVector to pStats.
    1879  void AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const;
    1880 
    1881 #if VMA_STATS_STRING_ENABLED
    1882  void PrintDetailedMap(class VmaStringBuilder& sb) const;
    1883 #endif
    1884 
    1885 private:
    1886  VmaAllocator m_hAllocator;
    1887 };
    1888 
    1889 // Main allocator object.
    1890 struct VmaAllocator_T
    1891 {
    1892  VkDevice m_hDevice;
    1893  bool m_AllocationCallbacksSpecified;
    1894  VkAllocationCallbacks m_AllocationCallbacks;
    1895  VkDeviceSize m_PreferredLargeHeapBlockSize;
    1896  VkDeviceSize m_PreferredSmallHeapBlockSize;
    1897 
    1898  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
    1899  VkPhysicalDeviceMemoryProperties m_MemProps;
    1900 
    1901  VmaAllocationVector* m_pAllocations[VK_MAX_MEMORY_TYPES];
    1902  /* There can be at most one allocation that is completely empty - a
    1903  hysteresis to avoid pessimistic case of alternating creation and destruction
    1904  of a VkDeviceMemory. */
    1905  bool m_HasEmptyAllocation[VK_MAX_MEMORY_TYPES];
    1906  VmaMutex m_AllocationsMutex[VK_MAX_MEMORY_TYPES];
    1907 
    1908  // Each vector is sorted by memory (handle value).
    1909  typedef VmaVector< VmaOwnAllocation, VmaStlAllocator<VmaOwnAllocation> > OwnAllocationVectorType;
    1910  OwnAllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES];
    1911  VmaMutex m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES];
    1912 
    1913  // Sorted by first (VkBuffer handle value).
    1914  VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange) m_BufferToMemoryMap;
    1915  VmaMutex m_BufferToMemoryMapMutex;
    1916  // Sorted by first (VkImage handle value).
    1917  VMA_MAP_TYPE(VkImage, VkMappedMemoryRange) m_ImageToMemoryMap;
    1918  VmaMutex m_ImageToMemoryMapMutex;
    1919 
    1920  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
    1921  ~VmaAllocator_T();
    1922 
    1923  const VkAllocationCallbacks* GetAllocationCallbacks() const
    1924  {
    1925  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
    1926  }
    1927 
    1928  VkDeviceSize GetPreferredBlockSize(uint32_t memTypeIndex) const;
    1929 
    1930  VkDeviceSize GetBufferImageGranularity() const
    1931  {
    1932  return VMA_MAX(
    1933  VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY,
    1934  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
    1935  }
    1936 
    1937  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
    1938  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
    1939 
    1940  // Main allocation function.
    1941  VkResult AllocateMemory(
    1942  const VkMemoryRequirements& vkMemReq,
    1943  const VmaMemoryRequirements& vmaMemReq,
    1944  VmaSuballocationType suballocType,
    1945  VkMappedMemoryRange* pMemory,
    1946  uint32_t* pMemoryTypeIndex);
    1947 
    1948  // Main deallocation function.
    1949  void FreeMemory(const VkMappedMemoryRange* pMemory);
    1950 
    1951  void CalculateStats(VmaStats* pStats);
    1952 
    1953 #if VMA_STATS_STRING_ENABLED
    1954  void PrintDetailedMap(class VmaStringBuilder& sb);
    1955 #endif
    1956 
    1957 private:
    1958  VkPhysicalDevice m_PhysicalDevice;
    1959 
    1960  VkResult AllocateMemoryOfType(
    1961  const VkMemoryRequirements& vkMemReq,
    1962  const VmaMemoryRequirements& vmaMemReq,
    1963  uint32_t memTypeIndex,
    1964  VmaSuballocationType suballocType,
    1965  VkMappedMemoryRange* pMemory);
    1966 
    1967  // Allocates and registers new VkDeviceMemory specifically for single allocation.
    1968  VkResult AllocateOwnMemory(
    1969  VkDeviceSize size,
    1970  VmaSuballocationType suballocType,
    1971  uint32_t memTypeIndex,
    1972  VkMappedMemoryRange* pMemory);
    1973 
    1974  // Tries to free pMemory as Own Memory. Returns true if found and freed.
    1975  bool FreeOwnMemory(const VkMappedMemoryRange* pMemory);
    1976 };
    1977 
    1979 // Memory allocation #2 after VmaAllocator_T definition
    1980 
    1981 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
    1982 {
    1983  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
    1984 }
    1985 
    1986 static void VmaFree(VmaAllocator hAllocator, void* ptr)
    1987 {
    1988  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
    1989 }
    1990 
    1991 template<typename T>
    1992 static T* VmaAllocate(VmaAllocator hAllocator)
    1993 {
    1994  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
    1995 }
    1996 
    1997 template<typename T>
    1998 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
    1999 {
    2000  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
    2001 }
    2002 
    2003 template<typename T>
    2004 static void vma_delete(VmaAllocator hAllocator, T* ptr)
    2005 {
    2006  if(ptr != VMA_NULL)
    2007  {
    2008  ptr->~T();
    2009  VmaFree(hAllocator, ptr);
    2010  }
    2011 }
    2012 
    2013 template<typename T>
    2014 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
    2015 {
    2016  if(ptr != VMA_NULL)
    2017  {
    2018  for(size_t i = count; i--; )
    2019  ptr[i].~T();
    2020  VmaFree(hAllocator, ptr);
    2021  }
    2022 }
    2023 
    2025 // VmaStringBuilder
    2026 
    2027 #if VMA_STATS_STRING_ENABLED
    2028 
    2029 class VmaStringBuilder
    2030 {
    2031 public:
    2032  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
    2033  size_t GetLength() const { return m_Data.size(); }
    2034  const char* GetData() const { return m_Data.data(); }
    2035 
    2036  void Add(char ch) { m_Data.push_back(ch); }
    2037  void Add(const char* pStr);
    2038  void AddNewLine() { Add('\n'); }
    2039  void AddNumber(uint32_t num);
    2040  void AddNumber(uint64_t num);
    2041  void AddBool(bool b) { Add(b ? "true" : "false"); }
    2042  void AddNull() { Add("null"); }
    2043  void AddString(const char* pStr);
    2044 
    2045 private:
    2046  VmaVector< char, VmaStlAllocator<char> > m_Data;
    2047 };
    2048 
    2049 void VmaStringBuilder::Add(const char* pStr)
    2050 {
    2051  const size_t strLen = strlen(pStr);
    2052  if(strLen > 0)
    2053  {
    2054  const size_t oldCount = m_Data.size();
    2055  m_Data.resize(oldCount + strLen);
    2056  memcpy(m_Data.data() + oldCount, pStr, strLen);
    2057  }
    2058 }
    2059 
    2060 void VmaStringBuilder::AddNumber(uint32_t num)
    2061 {
    2062  char buf[11];
    2063  VmaUint32ToStr(buf, sizeof(buf), num);
    2064  Add(buf);
    2065 }
    2066 
    2067 void VmaStringBuilder::AddNumber(uint64_t num)
    2068 {
    2069  char buf[21];
    2070  VmaUint64ToStr(buf, sizeof(buf), num);
    2071  Add(buf);
    2072 }
    2073 
    2074 void VmaStringBuilder::AddString(const char* pStr)
    2075 {
    2076  Add('"');
    2077  const size_t strLen = strlen(pStr);
    2078  for(size_t i = 0; i < strLen; ++i)
    2079  {
    2080  char ch = pStr[i];
    2081  if(ch == '\'')
    2082  Add("\\\\");
    2083  else if(ch == '"')
    2084  Add("\\\"");
    2085  else if(ch >= 32)
    2086  Add(ch);
    2087  else switch(ch)
    2088  {
    2089  case '\n':
    2090  Add("\\n");
    2091  break;
    2092  case '\r':
    2093  Add("\\r");
    2094  break;
    2095  case '\t':
    2096  Add("\\t");
    2097  break;
    2098  default:
    2099  VMA_ASSERT(0 && "Character not currently supported.");
    2100  break;
    2101  }
    2102  }
    2103  Add('"');
    2104 }
    2105 
    2107 
    2108 // Correspond to values of enum VmaSuballocationType.
    2109 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
    2110  "FREE",
    2111  "UNKNOWN",
    2112  "BUFFER",
    2113  "IMAGE_UNKNOWN",
    2114  "IMAGE_LINEAR",
    2115  "IMAGE_OPTIMAL",
    2116 };
    2117 
    2118 static void VmaPrintStatInfo(VmaStringBuilder& sb, const VmaStatInfo& stat)
    2119 {
    2120  sb.Add("{ \"Allocations\": ");
    2121  sb.AddNumber(stat.AllocationCount);
    2122  sb.Add(", \"Suballocations\": ");
    2123  sb.AddNumber(stat.SuballocationCount);
    2124  sb.Add(", \"UnusedRanges\": ");
    2125  sb.AddNumber(stat.UnusedRangeCount);
    2126  sb.Add(", \"UsedBytes\": ");
    2127  sb.AddNumber(stat.UsedBytes);
    2128  sb.Add(", \"UnusedBytes\": ");
    2129  sb.AddNumber(stat.UnusedBytes);
    2130  sb.Add(", \"SuballocationSize\": { \"Min\": ");
    2131  sb.AddNumber(stat.SuballocationSizeMin);
    2132  sb.Add(", \"Avg\": ");
    2133  sb.AddNumber(stat.SuballocationSizeAvg);
    2134  sb.Add(", \"Max\": ");
    2135  sb.AddNumber(stat.SuballocationSizeMax);
    2136  sb.Add(" }, \"UnusedRangeSize\": { \"Min\": ");
    2137  sb.AddNumber(stat.UnusedRangeSizeMin);
    2138  sb.Add(", \"Avg\": ");
    2139  sb.AddNumber(stat.UnusedRangeSizeAvg);
    2140  sb.Add(", \"Max\": ");
    2141  sb.AddNumber(stat.UnusedRangeSizeMax);
    2142  sb.Add(" } }");
    2143 }
    2144 
    2145 #endif // #if VMA_STATS_STRING_ENABLED
    2146 
    2147 struct VmaSuballocationItemSizeLess
    2148 {
    2149  bool operator()(
    2150  const VmaSuballocationList::iterator lhs,
    2151  const VmaSuballocationList::iterator rhs) const
    2152  {
    2153  return lhs->size < rhs->size;
    2154  }
    2155  bool operator()(
    2156  const VmaSuballocationList::iterator lhs,
    2157  VkDeviceSize rhsSize) const
    2158  {
    2159  return lhs->size < rhsSize;
    2160  }
    2161 };
    2162 
    2163 VmaAllocation::VmaAllocation(VmaAllocator hAllocator) :
    2164  m_hMemory(VK_NULL_HANDLE),
    2165  m_Size(0),
    2166  m_FreeCount(0),
    2167  m_SumFreeSize(0),
    2168  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
    2169  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
    2170 {
    2171 }
    2172 
    2173 void VmaAllocation::Init(VkDeviceMemory newMemory, VkDeviceSize newSize)
    2174 {
    2175  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
    2176 
    2177  m_hMemory = newMemory;
    2178  m_Size = newSize;
    2179  m_FreeCount = 1;
    2180  m_SumFreeSize = newSize;
    2181 
    2182  m_Suballocations.clear();
    2183  m_FreeSuballocationsBySize.clear();
    2184 
    2185  VmaSuballocation suballoc = {};
    2186  suballoc.offset = 0;
    2187  suballoc.size = newSize;
    2188  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    2189 
    2190  m_Suballocations.push_back(suballoc);
    2191  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
    2192  --suballocItem;
    2193  m_FreeSuballocationsBySize.push_back(suballocItem);
    2194 }
    2195 
    2196 void VmaAllocation::Destroy(VmaAllocator allocator)
    2197 {
    2198  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
    2199  vkFreeMemory(allocator->m_hDevice, m_hMemory, allocator->GetAllocationCallbacks());
    2200  m_hMemory = VK_NULL_HANDLE;
    2201 }
    2202 
    2203 bool VmaAllocation::Validate() const
    2204 {
    2205  if((m_hMemory == VK_NULL_HANDLE) ||
    2206  (m_Size == 0) ||
    2207  m_Suballocations.empty())
    2208  {
    2209  return false;
    2210  }
    2211 
    2212  // Expected offset of new suballocation as calculates from previous ones.
    2213  VkDeviceSize calculatedOffset = 0;
    2214  // Expected number of free suballocations as calculated from traversing their list.
    2215  uint32_t calculatedFreeCount = 0;
    2216  // Expected sum size of free suballocations as calculated from traversing their list.
    2217  VkDeviceSize calculatedSumFreeSize = 0;
    2218  // Expected number of free suballocations that should be registered in
    2219  // m_FreeSuballocationsBySize calculated from traversing their list.
    2220  size_t freeSuballocationsToRegister = 0;
    2221  // True if previous visisted suballocation was free.
    2222  bool prevFree = false;
    2223 
    2224  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
    2225  suballocItem != m_Suballocations.cend();
    2226  ++suballocItem)
    2227  {
    2228  const VmaSuballocation& subAlloc = *suballocItem;
    2229 
    2230  // Actual offset of this suballocation doesn't match expected one.
    2231  if(subAlloc.offset != calculatedOffset)
    2232  return false;
    2233 
    2234  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
    2235  // Two adjacent free suballocations are invalid. They should be merged.
    2236  if(prevFree && currFree)
    2237  return false;
    2238  prevFree = currFree;
    2239 
    2240  if(currFree)
    2241  {
    2242  calculatedSumFreeSize += subAlloc.size;
    2243  ++calculatedFreeCount;
    2244  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    2245  ++freeSuballocationsToRegister;
    2246  }
    2247 
    2248  calculatedOffset += subAlloc.size;
    2249  }
    2250 
    2251  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
    2252  // match expected one.
    2253  if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)
    2254  return false;
    2255 
    2256  VkDeviceSize lastSize = 0;
    2257  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
    2258  {
    2259  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
    2260 
    2261  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
    2262  if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
    2263  return false;
    2264  // They must be sorted by size ascending.
    2265  if(suballocItem->size < lastSize)
    2266  return false;
    2267 
    2268  lastSize = suballocItem->size;
    2269  }
    2270 
    2271  // Check if totals match calculacted values.
    2272  return
    2273  (calculatedOffset == m_Size) &&
    2274  (calculatedSumFreeSize == m_SumFreeSize) &&
    2275  (calculatedFreeCount == m_FreeCount);
    2276 }
    2277 
    2278 /*
    2279 How many suitable free suballocations to analyze before choosing best one.
    2280 - Set to 1 to use First-Fit algorithm - first suitable free suballocation will
    2281  be chosen.
    2282 - Set to UINT_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
    2283  suballocations will be analized and best one will be chosen.
    2284 - Any other value is also acceptable.
    2285 */
    2286 //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
    2287 
    2288 bool VmaAllocation::CreateAllocationRequest(
    2289  VkDeviceSize bufferImageGranularity,
    2290  VkDeviceSize allocSize,
    2291  VkDeviceSize allocAlignment,
    2292  VmaSuballocationType allocType,
    2293  VmaAllocationRequest* pAllocationRequest)
    2294 {
    2295  VMA_ASSERT(allocSize > 0);
    2296  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
    2297  VMA_ASSERT(pAllocationRequest != VMA_NULL);
    2298  VMA_HEAVY_ASSERT(Validate());
    2299 
    2300  // There is not enough total free space in this allocation to fullfill the request: Early return.
    2301  if(m_SumFreeSize < allocSize)
    2302  return false;
    2303 
    2304  bool found = false;
    2305 
    2306  // Old brute-force algorithm, linearly searching suballocations.
    2307  /*
    2308  uint32_t suitableSuballocationsFound = 0;
    2309  for(VmaSuballocationList::iterator suballocItem = suballocations.Front();
    2310  suballocItem != VMA_NULL &&
    2311  suitableSuballocationsFound < MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK;
    2312  suballocItem = suballocItem->Next)
    2313  {
    2314  if(suballocItem->Value.type == VMA_SUBALLOCATION_TYPE_FREE)
    2315  {
    2316  VkDeviceSize offset = 0, cost = 0;
    2317  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset, &cost))
    2318  {
    2319  ++suitableSuballocationsFound;
    2320  if(cost < costLimit)
    2321  {
    2322  pAllocationRequest->freeSuballocationItem = suballocItem;
    2323  pAllocationRequest->offset = offset;
    2324  pAllocationRequest->cost = cost;
    2325  if(cost == 0)
    2326  return true;
    2327  costLimit = cost;
    2328  betterSuballocationFound = true;
    2329  }
    2330  }
    2331  }
    2332  }
    2333  */
    2334 
    2335  // New algorithm, efficiently searching freeSuballocationsBySize.
    2336  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
    2337  if(freeSuballocCount > 0)
    2338  {
    2339  if(VMA_BEST_FIT)
    2340  {
    2341  // Find first free suballocation with size not less than allocSize.
    2342  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    2343  m_FreeSuballocationsBySize.data(),
    2344  m_FreeSuballocationsBySize.data() + freeSuballocCount,
    2345  allocSize,
    2346  VmaSuballocationItemSizeLess());
    2347  size_t index = it - m_FreeSuballocationsBySize.data();
    2348  for(; index < freeSuballocCount; ++index)
    2349  {
    2350  VkDeviceSize offset = 0;
    2351  const VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[index];
    2352  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset))
    2353  {
    2354  pAllocationRequest->freeSuballocationItem = suballocItem;
    2355  pAllocationRequest->offset = offset;
    2356  return true;
    2357  }
    2358  }
    2359  }
    2360  else
    2361  {
    2362  // Search staring from biggest suballocations.
    2363  for(size_t index = freeSuballocCount; index--; )
    2364  {
    2365  VkDeviceSize offset = 0;
    2366  const VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[index];
    2367  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset))
    2368  {
    2369  pAllocationRequest->freeSuballocationItem = suballocItem;
    2370  pAllocationRequest->offset = offset;
    2371  return true;
    2372  }
    2373  }
    2374  }
    2375  }
    2376 
    2377  return false;
    2378 }
    2379 
    2380 bool VmaAllocation::CheckAllocation(
    2381  VkDeviceSize bufferImageGranularity,
    2382  VkDeviceSize allocSize,
    2383  VkDeviceSize allocAlignment,
    2384  VmaSuballocationType allocType,
    2385  VmaSuballocationList::const_iterator freeSuballocItem,
    2386  VkDeviceSize* pOffset) const
    2387 {
    2388  VMA_ASSERT(allocSize > 0);
    2389  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
    2390  VMA_ASSERT(freeSuballocItem != m_Suballocations.cend());
    2391  VMA_ASSERT(pOffset != VMA_NULL);
    2392 
    2393  const VmaSuballocation& suballoc = *freeSuballocItem;
    2394  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
    2395 
    2396  // Size of this suballocation is too small for this request: Early return.
    2397  if(suballoc.size < allocSize)
    2398  return false;
    2399 
    2400  // Start from offset equal to beginning of this suballocation.
    2401  *pOffset = suballoc.offset;
    2402 
    2403  // Apply VMA_DEBUG_MARGIN at the beginning.
    2404  if((VMA_DEBUG_MARGIN > 0) && freeSuballocItem != m_Suballocations.cbegin())
    2405  *pOffset += VMA_DEBUG_MARGIN;
    2406 
    2407  // Apply alignment.
    2408  const VkDeviceSize alignment = VMA_MAX(allocAlignment, VMA_DEBUG_ALIGNMENT);
    2409  *pOffset = VmaAlignUp(*pOffset, alignment);
    2410 
    2411  // Check previous suballocations for BufferImageGranularity conflicts.
    2412  // Make bigger alignment if necessary.
    2413  if(bufferImageGranularity > 1)
    2414  {
    2415  bool bufferImageGranularityConflict = false;
    2416  VmaSuballocationList::const_iterator prevSuballocItem = freeSuballocItem;
    2417  while(prevSuballocItem != m_Suballocations.cbegin())
    2418  {
    2419  --prevSuballocItem;
    2420  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
    2421  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
    2422  {
    2423  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
    2424  {
    2425  bufferImageGranularityConflict = true;
    2426  break;
    2427  }
    2428  }
    2429  else
    2430  // Already on previous page.
    2431  break;
    2432  }
    2433  if(bufferImageGranularityConflict)
    2434  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
    2435  }
    2436 
    2437  // Calculate padding at the beginning based on current offset.
    2438  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
    2439 
    2440  // Calculate required margin at the end if this is not last suballocation.
    2441  VmaSuballocationList::const_iterator next = freeSuballocItem;
    2442  ++next;
    2443  const VkDeviceSize requiredEndMargin =
    2444  (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
    2445 
    2446  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
    2447  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
    2448  return false;
    2449 
    2450  // Check next suballocations for BufferImageGranularity conflicts.
    2451  // If conflict exists, allocation cannot be made here.
    2452  if(bufferImageGranularity > 1)
    2453  {
    2454  VmaSuballocationList::const_iterator nextSuballocItem = freeSuballocItem;
    2455  ++nextSuballocItem;
    2456  while(nextSuballocItem != m_Suballocations.cend())
    2457  {
    2458  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
    2459  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
    2460  {
    2461  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
    2462  return false;
    2463  }
    2464  else
    2465  // Already on next page.
    2466  break;
    2467  ++nextSuballocItem;
    2468  }
    2469  }
    2470 
    2471  // All tests passed: Success. pOffset is already filled.
    2472  return true;
    2473 }
    2474 
    2475 bool VmaAllocation::IsEmpty() const
    2476 {
    2477  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
    2478 }
    2479 
    2480 void VmaAllocation::Alloc(
    2481  const VmaAllocationRequest& request,
    2482  VmaSuballocationType type,
    2483  VkDeviceSize allocSize)
    2484 {
    2485  VMA_ASSERT(request.freeSuballocationItem != m_Suballocations.end());
    2486  VmaSuballocation& suballoc = *request.freeSuballocationItem;
    2487  // Given suballocation is a free block.
    2488  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
    2489  // Given offset is inside this suballocation.
    2490  VMA_ASSERT(request.offset >= suballoc.offset);
    2491  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
    2492  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
    2493  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
    2494 
    2495  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
    2496  // it to become used.
    2497  UnregisterFreeSuballocation(request.freeSuballocationItem);
    2498 
    2499  suballoc.offset = request.offset;
    2500  suballoc.size = allocSize;
    2501  suballoc.type = type;
    2502 
    2503  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
    2504  if(paddingEnd)
    2505  {
    2506  VmaSuballocation paddingSuballoc = {};
    2507  paddingSuballoc.offset = request.offset + allocSize;
    2508  paddingSuballoc.size = paddingEnd;
    2509  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    2510  VmaSuballocationList::iterator next = request.freeSuballocationItem;
    2511  ++next;
    2512  const VmaSuballocationList::iterator paddingEndItem =
    2513  m_Suballocations.insert(next, paddingSuballoc);
    2514  RegisterFreeSuballocation(paddingEndItem);
    2515  }
    2516 
    2517  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
    2518  if(paddingBegin)
    2519  {
    2520  VmaSuballocation paddingSuballoc = {};
    2521  paddingSuballoc.offset = request.offset - paddingBegin;
    2522  paddingSuballoc.size = paddingBegin;
    2523  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    2524  const VmaSuballocationList::iterator paddingBeginItem =
    2525  m_Suballocations.insert(request.freeSuballocationItem, paddingSuballoc);
    2526  RegisterFreeSuballocation(paddingBeginItem);
    2527  }
    2528 
    2529  // Update totals.
    2530  m_FreeCount = m_FreeCount - 1;
    2531  if(paddingBegin > 0)
    2532  ++m_FreeCount;
    2533  if(paddingEnd > 0)
    2534  ++m_FreeCount;
    2535  m_SumFreeSize -= allocSize;
    2536 }
    2537 
    2538 void VmaAllocation::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
    2539 {
    2540  // Change this suballocation to be marked as free.
    2541  VmaSuballocation& suballoc = *suballocItem;
    2542  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    2543 
    2544  // Update totals.
    2545  ++m_FreeCount;
    2546  m_SumFreeSize += suballoc.size;
    2547 
    2548  // Merge with previous and/or next suballocation if it's also free.
    2549  bool mergeWithNext = false;
    2550  bool mergeWithPrev = false;
    2551 
    2552  VmaSuballocationList::iterator nextItem = suballocItem;
    2553  ++nextItem;
    2554  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
    2555  mergeWithNext = true;
    2556 
    2557  VmaSuballocationList::iterator prevItem = suballocItem;
    2558  if(suballocItem != m_Suballocations.begin())
    2559  {
    2560  --prevItem;
    2561  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
    2562  mergeWithPrev = true;
    2563  }
    2564 
    2565  if(mergeWithNext)
    2566  {
    2567  UnregisterFreeSuballocation(nextItem);
    2568  MergeFreeWithNext(suballocItem);
    2569  }
    2570 
    2571  if(mergeWithPrev)
    2572  {
    2573  UnregisterFreeSuballocation(prevItem);
    2574  MergeFreeWithNext(prevItem);
    2575  RegisterFreeSuballocation(prevItem);
    2576  }
    2577  else
    2578  RegisterFreeSuballocation(suballocItem);
    2579 }
    2580 
    2581 void VmaAllocation::Free(const VkMappedMemoryRange* pMemory)
    2582 {
    2583  // If suballocation to free has offset smaller than half of allocation size, search forward.
    2584  // Otherwise search backward.
    2585  const bool forwardDirection = pMemory->offset < (m_Size / 2);
    2586  if(forwardDirection)
    2587  {
    2588  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
    2589  suballocItem != m_Suballocations.end();
    2590  ++suballocItem)
    2591  {
    2592  VmaSuballocation& suballoc = *suballocItem;
    2593  if(suballoc.offset == pMemory->offset)
    2594  {
    2595  FreeSuballocation(suballocItem);
    2596  VMA_HEAVY_ASSERT(Validate());
    2597  return;
    2598  }
    2599  }
    2600  VMA_ASSERT(0 && "Not found!");
    2601  }
    2602  else
    2603  {
    2604  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
    2605  suballocItem != m_Suballocations.end();
    2606  ++suballocItem)
    2607  {
    2608  VmaSuballocation& suballoc = *suballocItem;
    2609  if(suballoc.offset == pMemory->offset)
    2610  {
    2611  FreeSuballocation(suballocItem);
    2612  VMA_HEAVY_ASSERT(Validate());
    2613  return;
    2614  }
    2615  }
    2616  VMA_ASSERT(0 && "Not found!");
    2617  }
    2618 }
    2619 
    2620 #if VMA_STATS_STRING_ENABLED
    2621 
    2622 void VmaAllocation::PrintDetailedMap(class VmaStringBuilder& sb) const
    2623 {
    2624  sb.Add("{\n\t\t\t\"Bytes\": ");
    2625  sb.AddNumber(m_Size);
    2626  sb.Add(",\n\t\t\t\"FreeBytes\": ");
    2627  sb.AddNumber(m_SumFreeSize);
    2628  sb.Add(",\n\t\t\t\"Suballocations\": ");
    2629  sb.AddNumber(m_Suballocations.size());
    2630  sb.Add(",\n\t\t\t\"FreeSuballocations\": ");
    2631  sb.AddNumber(m_FreeCount);
    2632  sb.Add(",\n\t\t\t\"SuballocationList\": [");
    2633 
    2634  size_t i = 0;
    2635  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
    2636  suballocItem != m_Suballocations.cend();
    2637  ++suballocItem, ++i)
    2638  {
    2639  if(i > 0)
    2640  sb.Add(",\n\t\t\t\t{ \"Type\": ");
    2641  else
    2642  sb.Add("\n\t\t\t\t{ \"Type\": ");
    2643  sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]);
    2644  sb.Add(", \"Size\": ");
    2645  sb.AddNumber(suballocItem->size);
    2646  sb.Add(", \"Offset\": ");
    2647  sb.AddNumber(suballocItem->offset);
    2648  sb.Add(" }");
    2649  }
    2650 
    2651  sb.Add("\n\t\t\t]\n\t\t}");
    2652 }
    2653 
    2654 #endif // #if VMA_STATS_STRING_ENABLED
    2655 
    2656 void VmaAllocation::MergeFreeWithNext(VmaSuballocationList::iterator item)
    2657 {
    2658  VMA_ASSERT(item != m_Suballocations.end());
    2659  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    2660 
    2661  VmaSuballocationList::iterator nextItem = item;
    2662  ++nextItem;
    2663  VMA_ASSERT(nextItem != m_Suballocations.end());
    2664  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
    2665 
    2666  item->size += nextItem->size;
    2667  --m_FreeCount;
    2668  m_Suballocations.erase(nextItem);
    2669 }
    2670 
    2671 void VmaAllocation::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
    2672 {
    2673  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    2674  VMA_ASSERT(item->size > 0);
    2675 
    2676  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    2677  {
    2678  if(m_FreeSuballocationsBySize.empty())
    2679  m_FreeSuballocationsBySize.push_back(item);
    2680  else
    2681  {
    2682  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    2683  m_FreeSuballocationsBySize.data(),
    2684  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
    2685  item,
    2686  VmaSuballocationItemSizeLess());
    2687  size_t index = it - m_FreeSuballocationsBySize.data();
    2688  VectorInsert(m_FreeSuballocationsBySize, index, item);
    2689  }
    2690  }
    2691 }
    2692 
    2693 void VmaAllocation::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
    2694 {
    2695  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    2696  VMA_ASSERT(item->size > 0);
    2697 
    2698  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    2699  {
    2700  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    2701  m_FreeSuballocationsBySize.data(),
    2702  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
    2703  item,
    2704  VmaSuballocationItemSizeLess());
    2705  for(size_t index = it - m_FreeSuballocationsBySize.data();
    2706  index < m_FreeSuballocationsBySize.size();
    2707  ++index)
    2708  {
    2709  if(m_FreeSuballocationsBySize[index] == item)
    2710  {
    2711  VectorRemove(m_FreeSuballocationsBySize, index);
    2712  return;
    2713  }
    2714  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
    2715  }
    2716  VMA_ASSERT(0 && "Not found.");
    2717  }
    2718 }
    2719 
    2720 static void InitStatInfo(VmaStatInfo& outInfo)
    2721 {
    2722  memset(&outInfo, 0, sizeof(outInfo));
    2723  outInfo.SuballocationSizeMin = UINT64_MAX;
    2724  outInfo.UnusedRangeSizeMin = UINT64_MAX;
    2725 }
    2726 
    2727 static void CalcAllocationStatInfo(VmaStatInfo& outInfo, const VmaAllocation& alloc)
    2728 {
    2729  outInfo.AllocationCount = 1;
    2730 
    2731  const uint32_t rangeCount = (uint32_t)alloc.m_Suballocations.size();
    2732  outInfo.SuballocationCount = rangeCount - alloc.m_FreeCount;
    2733  outInfo.UnusedRangeCount = alloc.m_FreeCount;
    2734 
    2735  outInfo.UnusedBytes = alloc.m_SumFreeSize;
    2736  outInfo.UsedBytes = alloc.m_Size - outInfo.UnusedBytes;
    2737 
    2738  outInfo.SuballocationSizeMin = UINT64_MAX;
    2739  outInfo.SuballocationSizeMax = 0;
    2740  outInfo.UnusedRangeSizeMin = UINT64_MAX;
    2741  outInfo.UnusedRangeSizeMax = 0;
    2742 
    2743  for(VmaSuballocationList::const_iterator suballocItem = alloc.m_Suballocations.cbegin();
    2744  suballocItem != alloc.m_Suballocations.cend();
    2745  ++suballocItem)
    2746  {
    2747  const VmaSuballocation& suballoc = *suballocItem;
    2748  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
    2749  {
    2750  outInfo.SuballocationSizeMin = VMA_MIN(outInfo.SuballocationSizeMin, suballoc.size);
    2751  outInfo.SuballocationSizeMax = VMA_MAX(outInfo.SuballocationSizeMax, suballoc.size);
    2752  }
    2753  else
    2754  {
    2755  outInfo.UnusedRangeSizeMin = VMA_MIN(outInfo.UnusedRangeSizeMin, suballoc.size);
    2756  outInfo.UnusedRangeSizeMax = VMA_MAX(outInfo.UnusedRangeSizeMax, suballoc.size);
    2757  }
    2758  }
    2759 }
    2760 
    2761 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
    2762 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
    2763 {
    2764  inoutInfo.AllocationCount += srcInfo.AllocationCount;
    2765  inoutInfo.SuballocationCount += srcInfo.SuballocationCount;
    2766  inoutInfo.UnusedRangeCount += srcInfo.UnusedRangeCount;
    2767  inoutInfo.UsedBytes += srcInfo.UsedBytes;
    2768  inoutInfo.UnusedBytes += srcInfo.UnusedBytes;
    2769  inoutInfo.SuballocationSizeMin = VMA_MIN(inoutInfo.SuballocationSizeMin, srcInfo.SuballocationSizeMin);
    2770  inoutInfo.SuballocationSizeMax = VMA_MAX(inoutInfo.SuballocationSizeMax, srcInfo.SuballocationSizeMax);
    2771  inoutInfo.UnusedRangeSizeMin = VMA_MIN(inoutInfo.UnusedRangeSizeMin, srcInfo.UnusedRangeSizeMin);
    2772  inoutInfo.UnusedRangeSizeMax = VMA_MAX(inoutInfo.UnusedRangeSizeMax, srcInfo.UnusedRangeSizeMax);
    2773 }
    2774 
    2775 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
    2776 {
    2777  inoutInfo.SuballocationSizeAvg = (inoutInfo.SuballocationCount > 0) ?
    2778  VmaRoundDiv<VkDeviceSize>(inoutInfo.UsedBytes, inoutInfo.SuballocationCount) : 0;
    2779  inoutInfo.UnusedRangeSizeAvg = (inoutInfo.UnusedRangeCount > 0) ?
    2780  VmaRoundDiv<VkDeviceSize>(inoutInfo.UnusedBytes, inoutInfo.UnusedRangeCount) : 0;
    2781 }
    2782 
    2783 VmaAllocationVector::VmaAllocationVector(VmaAllocator hAllocator) :
    2784  m_hAllocator(hAllocator),
    2785  m_Allocations(VmaStlAllocator<VmaAllocation*>(hAllocator->GetAllocationCallbacks()))
    2786 {
    2787 }
    2788 
    2789 VmaAllocationVector::~VmaAllocationVector()
    2790 {
    2791  for(size_t i = m_Allocations.size(); i--; )
    2792  {
    2793  m_Allocations[i]->Destroy(m_hAllocator);
    2794  vma_delete(m_hAllocator, m_Allocations[i]);
    2795  }
    2796 }
    2797 
    2798 size_t VmaAllocationVector::Free(const VkMappedMemoryRange* pMemory)
    2799 {
    2800  for(uint32_t allocIndex = 0; allocIndex < m_Allocations.size(); ++allocIndex)
    2801  {
    2802  VmaAllocation* const pAlloc = m_Allocations[allocIndex];
    2803  VMA_ASSERT(pAlloc);
    2804  if(pAlloc->m_hMemory == pMemory->memory)
    2805  {
    2806  pAlloc->Free(pMemory);
    2807  VMA_HEAVY_ASSERT(pAlloc->Validate());
    2808  return allocIndex;
    2809  }
    2810  }
    2811 
    2812  return (size_t)-1;
    2813 }
    2814 
    2815 void VmaAllocationVector::IncrementallySortAllocations()
    2816 {
    2817  // Bubble sort only until first swap.
    2818  for(size_t i = 1; i < m_Allocations.size(); ++i)
    2819  {
    2820  if(m_Allocations[i - 1]->m_SumFreeSize > m_Allocations[i]->m_SumFreeSize)
    2821  {
    2822  VMA_SWAP(m_Allocations[i - 1], m_Allocations[i]);
    2823  return;
    2824  }
    2825  }
    2826 }
    2827 
    2828 #if VMA_STATS_STRING_ENABLED
    2829 
    2830 void VmaAllocationVector::PrintDetailedMap(class VmaStringBuilder& sb) const
    2831 {
    2832  for(size_t i = 0; i < m_Allocations.size(); ++i)
    2833  {
    2834  if(i > 0)
    2835  sb.Add(",\n\t\t");
    2836  else
    2837  sb.Add("\n\t\t");
    2838  m_Allocations[i]->PrintDetailedMap(sb);
    2839  }
    2840 }
    2841 
    2842 #endif // #if VMA_STATS_STRING_ENABLED
    2843 
    2844 void VmaAllocationVector::AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const
    2845 {
    2846  for(uint32_t allocIndex = 0; allocIndex < m_Allocations.size(); ++allocIndex)
    2847  {
    2848  const VmaAllocation* const pAlloc = m_Allocations[allocIndex];
    2849  VMA_ASSERT(pAlloc);
    2850  VMA_HEAVY_ASSERT(pAlloc->Validate());
    2851  VmaStatInfo allocationStatInfo;
    2852  CalcAllocationStatInfo(allocationStatInfo, *pAlloc);
    2853  VmaAddStatInfo(pStats->total, allocationStatInfo);
    2854  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
    2855  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
    2856  }
    2857 }
    2858 
    2860 // VmaAllocator_T
    2861 
    2862 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
    2863  m_PhysicalDevice(pCreateInfo->physicalDevice),
    2864  m_hDevice(pCreateInfo->device),
    2865  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
    2866  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
    2867  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
    2868  m_PreferredLargeHeapBlockSize(0),
    2869  m_PreferredSmallHeapBlockSize(0),
    2870  m_BufferToMemoryMap(VmaStlAllocator< VmaPair<VkBuffer, VkMappedMemoryRange> >(pCreateInfo->pAllocationCallbacks)),
    2871  m_ImageToMemoryMap(VmaStlAllocator< VmaPair<VkImage, VkMappedMemoryRange> >(pCreateInfo->pAllocationCallbacks))
    2872 {
    2873  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
    2874 
    2875  memset(&m_MemProps, 0, sizeof(m_MemProps));
    2876  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
    2877 
    2878  memset(&m_pAllocations, 0, sizeof(m_pAllocations));
    2879  memset(&m_HasEmptyAllocation, 0, sizeof(m_HasEmptyAllocation));
    2880  memset(&m_pOwnAllocations, 0, sizeof(m_pOwnAllocations));
    2881 
    2882  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
    2883  pCreateInfo->preferredLargeHeapBlockSize : VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE;
    2884  m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ?
    2885  pCreateInfo->preferredSmallHeapBlockSize : VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE;
    2886 
    2887  vkGetPhysicalDeviceProperties(m_PhysicalDevice, &m_PhysicalDeviceProperties);
    2888  vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &m_MemProps);
    2889 
    2890  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
    2891  {
    2892  m_pAllocations[i] = vma_new(this, VmaAllocationVector)(this);
    2893  m_pOwnAllocations[i] = vma_new(this, OwnAllocationVectorType)(VmaStlAllocator<VmaOwnAllocation>(GetAllocationCallbacks()));
    2894  }
    2895 }
    2896 
    2897 VmaAllocator_T::~VmaAllocator_T()
    2898 {
    2899  for(VMA_MAP_TYPE(VkImage, VkMappedMemoryRange)::iterator it = m_ImageToMemoryMap.begin();
    2900  it != m_ImageToMemoryMap.end();
    2901  ++it)
    2902  {
    2903  vkDestroyImage(m_hDevice, it->first, GetAllocationCallbacks());
    2904  }
    2905 
    2906  for(VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange)::iterator it = m_BufferToMemoryMap.begin();
    2907  it != m_BufferToMemoryMap.end();
    2908  ++it)
    2909  {
    2910  vkDestroyBuffer(m_hDevice, it->first, GetAllocationCallbacks());
    2911  }
    2912 
    2913  for(uint32_t typeIndex = 0; typeIndex < GetMemoryTypeCount(); ++typeIndex)
    2914  {
    2915  OwnAllocationVectorType* pOwnAllocations = m_pOwnAllocations[typeIndex];
    2916  VMA_ASSERT(pOwnAllocations);
    2917  for(size_t allocationIndex = 0; allocationIndex < pOwnAllocations->size(); ++allocationIndex)
    2918  {
    2919  const VmaOwnAllocation& ownAlloc = (*pOwnAllocations)[allocationIndex];
    2920  vkFreeMemory(m_hDevice, ownAlloc.m_hMemory, GetAllocationCallbacks());
    2921  }
    2922  }
    2923 
    2924  for(size_t i = GetMemoryTypeCount(); i--; )
    2925  {
    2926  vma_delete(this, m_pAllocations[i]);
    2927  vma_delete(this, m_pOwnAllocations[i]);
    2928  }
    2929 }
    2930 
    2931 VkDeviceSize VmaAllocator_T::GetPreferredBlockSize(uint32_t memTypeIndex) const
    2932 {
    2933  VkDeviceSize heapSize = m_MemProps.memoryHeaps[m_MemProps.memoryTypes[memTypeIndex].heapIndex].size;
    2934  return (heapSize <= VMA_SMALL_HEAP_MAX_SIZE) ?
    2935  m_PreferredSmallHeapBlockSize : m_PreferredLargeHeapBlockSize;
    2936 }
    2937 
    2938 VkResult VmaAllocator_T::AllocateMemoryOfType(
    2939  const VkMemoryRequirements& vkMemReq,
    2940  const VmaMemoryRequirements& vmaMemReq,
    2941  uint32_t memTypeIndex,
    2942  VmaSuballocationType suballocType,
    2943  VkMappedMemoryRange* pMemory)
    2944 {
    2945  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size);
    2946 
    2947  pMemory->sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    2948  pMemory->pNext = VMA_NULL;
    2949  pMemory->size = vkMemReq.size;
    2950 
    2951  const VkDeviceSize preferredBlockSize = GetPreferredBlockSize(memTypeIndex);
    2952  // Heuristics: Allocate own memory if requested size if greater than half of preferred block size.
    2953  const bool ownMemory =
    2954  vmaMemReq.ownMemory ||
    2955  VMA_DEBUG_ALWAYS_OWN_MEMORY ||
    2956  ((vmaMemReq.neverAllocate == false) && (vkMemReq.size > preferredBlockSize / 2));
    2957 
    2958  if(ownMemory)
    2959  {
    2960  if(vmaMemReq.neverAllocate)
    2961  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    2962  else
    2963  return AllocateOwnMemory(vkMemReq.size, suballocType, memTypeIndex, pMemory);
    2964  }
    2965  else
    2966  {
    2967  VmaMutexLock lock(m_AllocationsMutex[memTypeIndex]);
    2968  VmaAllocationVector* const allocationVector = m_pAllocations[memTypeIndex];
    2969  VMA_ASSERT(allocationVector);
    2970 
    2971  // 1. Search existing allocations.
    2972  // Forward order - prefer blocks with smallest amount of free space.
    2973  for(size_t allocIndex = 0; allocIndex < allocationVector->m_Allocations.size(); ++allocIndex )
    2974  {
    2975  VmaAllocation* const pAlloc = allocationVector->m_Allocations[allocIndex];
    2976  VMA_ASSERT(pAlloc);
    2977  VmaAllocationRequest allocRequest = {};
    2978  // Check if can allocate from pAlloc.
    2979  if(pAlloc->CreateAllocationRequest(
    2980  GetBufferImageGranularity(),
    2981  vkMemReq.size,
    2982  vkMemReq.alignment,
    2983  suballocType,
    2984  &allocRequest))
    2985  {
    2986  // We no longer have an empty Allocation.
    2987  if(pAlloc->IsEmpty())
    2988  m_HasEmptyAllocation[memTypeIndex] = false;
    2989  // Allocate from this pAlloc.
    2990  pAlloc->Alloc(allocRequest, suballocType, vkMemReq.size);
    2991  // Return VkDeviceMemory and offset (size already filled above).
    2992  pMemory->memory = pAlloc->m_hMemory;
    2993  pMemory->offset = allocRequest.offset;
    2994  VMA_HEAVY_ASSERT(pAlloc->Validate());
    2995  VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)allocIndex);
    2996  return VK_SUCCESS;
    2997  }
    2998  }
    2999 
    3000  // 2. Create new Allocation.
    3001  if(vmaMemReq.neverAllocate)
    3002  {
    3003  VMA_DEBUG_LOG(" FAILED due to VmaMemoryRequirements::neverAllocate");
    3004  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    3005  }
    3006  else
    3007  {
    3008  // Start with full preferredBlockSize.
    3009  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
    3010  allocInfo.memoryTypeIndex = memTypeIndex;
    3011  allocInfo.allocationSize = preferredBlockSize;
    3012  VkDeviceMemory mem = VK_NULL_HANDLE;
    3013  VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    3014  if(res < 0)
    3015  {
    3016  // 3. Try half the size.
    3017  allocInfo.allocationSize /= 2;
    3018  if(allocInfo.allocationSize >= vkMemReq.size)
    3019  {
    3020  res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    3021  if(res < 0)
    3022  {
    3023  // 4. Try quarter the size.
    3024  allocInfo.allocationSize /= 2;
    3025  if(allocInfo.allocationSize >= vkMemReq.size)
    3026  {
    3027  res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    3028  }
    3029  }
    3030  }
    3031  }
    3032  if(res < 0)
    3033  {
    3034  // 5. Try OwnAlloc.
    3035  res = AllocateOwnMemory(vkMemReq.size, suballocType, memTypeIndex, pMemory);
    3036  if(res == VK_SUCCESS)
    3037  {
    3038  // Succeeded: AllocateOwnMemory function already filld pMemory, nothing more to do here.
    3039  VMA_DEBUG_LOG(" Allocated as OwnMemory");
    3040  return VK_SUCCESS;
    3041  }
    3042  else
    3043  {
    3044  // Everything failed: Return error code.
    3045  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
    3046  return res;
    3047  }
    3048  }
    3049 
    3050  // New VkDeviceMemory successfully created. Create new Allocation for it.
    3051  VmaAllocation* const pAlloc = vma_new(this, VmaAllocation)(this);
    3052  pAlloc->Init(mem, allocInfo.allocationSize);
    3053 
    3054  allocationVector->m_Allocations.push_back(pAlloc);
    3055 
    3056  // Allocate from pAlloc. Because it is empty, allocRequest can be trivially filled.
    3057  VmaAllocationRequest allocRequest = {};
    3058  allocRequest.freeSuballocationItem = pAlloc->m_Suballocations.begin();
    3059  allocRequest.offset = 0;
    3060  pAlloc->Alloc(allocRequest, suballocType, vkMemReq.size);
    3061  pMemory->memory = mem;
    3062  pMemory->offset = allocRequest.offset;
    3063  VMA_HEAVY_ASSERT(pAlloc->Validate());
    3064  VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
    3065  return VK_SUCCESS;
    3066  }
    3067  }
    3068 }
    3069 
    3070 VkResult VmaAllocator_T::AllocateOwnMemory(
    3071  VkDeviceSize size,
    3072  VmaSuballocationType suballocType,
    3073  uint32_t memTypeIndex,
    3074  VkMappedMemoryRange* pMemory)
    3075 {
    3076  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
    3077  allocInfo.memoryTypeIndex = memTypeIndex;
    3078  allocInfo.allocationSize = size;
    3079 
    3080  // Allocate VkDeviceMemory.
    3081  VmaOwnAllocation ownAlloc = {};
    3082  ownAlloc.m_Size = size;
    3083  ownAlloc.m_Type = suballocType;
    3084  VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &ownAlloc.m_hMemory);
    3085  if(res < 0)
    3086  {
    3087  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
    3088  return res;
    3089  }
    3090 
    3091  // Register it in m_pOwnAllocations.
    3092  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]);
    3093  OwnAllocationVectorType* ownAllocations = m_pOwnAllocations[memTypeIndex];
    3094  VMA_ASSERT(ownAllocations);
    3095  VmaOwnAllocation* const pOwnAllocationsBeg = ownAllocations->data();
    3096  VmaOwnAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + ownAllocations->size();
    3097  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
    3098  pOwnAllocationsBeg,
    3099  pOwnAllocationsEnd,
    3100  ownAlloc,
    3101  VmaOwnAllocationMemoryHandleLess()) - pOwnAllocationsBeg;
    3102  VectorInsert(*ownAllocations, indexToInsert, ownAlloc);
    3103 
    3104  // Return parameters of the allocation.
    3105  pMemory->sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
    3106  pMemory->pNext = VMA_NULL;
    3107  pMemory->memory = ownAlloc.m_hMemory;
    3108  pMemory->offset = 0;
    3109  pMemory->size = size;
    3110 
    3111  VMA_DEBUG_LOG(" Allocated OwnMemory MemoryTypeIndex=#%u", memTypeIndex);
    3112 
    3113  return VK_SUCCESS;
    3114 }
    3115 
    3116 VkResult VmaAllocator_T::AllocateMemory(
    3117  const VkMemoryRequirements& vkMemReq,
    3118  const VmaMemoryRequirements& vmaMemReq,
    3119  VmaSuballocationType suballocType,
    3120  VkMappedMemoryRange* pMemory,
    3121  uint32_t* pMemoryTypeIndex)
    3122 {
    3123  if(vmaMemReq.ownMemory && vmaMemReq.neverAllocate)
    3124  {
    3125  VMA_ASSERT(0 && "Specifying VmaMemoryRequirements::ownMemory && VmaMemoryRequirements::neverAllocate makes no sense.");
    3126  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    3127  }
    3128 
    3129  // Bit mask of memory Vulkan types acceptable for this allocation.
    3130  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
    3131  uint32_t memTypeIndex = UINT_MAX;
    3132  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex);
    3133  if(res == VK_SUCCESS)
    3134  {
    3135  res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pMemory);
    3136  // Succeeded on first try.
    3137  if(res == VK_SUCCESS)
    3138  {
    3139  if(pMemoryTypeIndex != VMA_NULL)
    3140  *pMemoryTypeIndex = memTypeIndex;
    3141  return res;
    3142  }
    3143  // Allocation from this memory type failed. Try other compatible memory types.
    3144  else
    3145  {
    3146  for(;;)
    3147  {
    3148  // Remove old memTypeIndex from list of possibilities.
    3149  memoryTypeBits &= ~(1u << memTypeIndex);
    3150  // Find alternative memTypeIndex.
    3151  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex);
    3152  if(res == VK_SUCCESS)
    3153  {
    3154  res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pMemory);
    3155  // Allocation from this alternative memory type succeeded.
    3156  if(res == VK_SUCCESS)
    3157  {
    3158  if(pMemoryTypeIndex != VMA_NULL)
    3159  *pMemoryTypeIndex = memTypeIndex;
    3160  return res;
    3161  }
    3162  // else: Allocation from this memory type failed. Try next one - next loop iteration.
    3163  }
    3164  // No other matching memory type index could be found.
    3165  else
    3166  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
    3167  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    3168  }
    3169  }
    3170  }
    3171  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
    3172  else
    3173  return res;
    3174 }
    3175 
    3176 void VmaAllocator_T::FreeMemory(const VkMappedMemoryRange* pMemory)
    3177 {
    3178  uint32_t memTypeIndex = 0;
    3179  bool found = false;
    3180  VmaAllocation* allocationToDelete = VMA_NULL;
    3181  // Check all memory types because we don't know which one does pMemory come from.
    3182  for(; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    3183  {
    3184  VmaMutexLock lock(m_AllocationsMutex[memTypeIndex]);
    3185  VmaAllocationVector* const pAllocationVector = m_pAllocations[memTypeIndex];
    3186  VMA_ASSERT(pAllocationVector);
    3187  // Try to free pMemory from pAllocationVector.
    3188  const size_t allocIndex = pAllocationVector->Free(pMemory);
    3189  if(allocIndex != (size_t)-1)
    3190  {
    3191  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
    3192  found = true;
    3193  VmaAllocation* const pAlloc = pAllocationVector->m_Allocations[allocIndex];
    3194  VMA_ASSERT(pAlloc);
    3195  // pAlloc became empty after this deallocation.
    3196  if(pAlloc->IsEmpty())
    3197  {
    3198  // Already has empty Allocation. We don't want to have two, so delete this one.
    3199  if(m_HasEmptyAllocation[memTypeIndex])
    3200  {
    3201  allocationToDelete = pAlloc;
    3202  VectorRemove(pAllocationVector->m_Allocations, allocIndex);
    3203  break;
    3204  }
    3205  // We now have first empty Allocation.
    3206  else
    3207  m_HasEmptyAllocation[memTypeIndex] = true;
    3208  }
    3209  // Must be called after allocIndex is used, because later it may become invalid!
    3210  pAllocationVector->IncrementallySortAllocations();
    3211  break;
    3212  }
    3213  }
    3214  if(found)
    3215  {
    3216  // Destruction of a free Allocation. Deferred until this point, outside of mutex
    3217  // lock, for performance reason.
    3218  if(allocationToDelete != VMA_NULL)
    3219  {
    3220  VMA_DEBUG_LOG(" Deleted empty allocation");
    3221  allocationToDelete->Destroy(this);
    3222  vma_delete(this, allocationToDelete);
    3223  }
    3224  return;
    3225  }
    3226 
    3227  // pMemory not found in allocations. Try free it as Own Memory.
    3228  if(FreeOwnMemory(pMemory))
    3229  return;
    3230 
    3231  // pMemory not found as Own Memory either.
    3232  VMA_ASSERT(0 && "Not found. Trying to free memory not allocated using this allocator (or some other bug).");
    3233 }
    3234 
    3235 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
    3236 {
    3237  InitStatInfo(pStats->total);
    3238  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
    3239  InitStatInfo(pStats->memoryType[i]);
    3240  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
    3241  InitStatInfo(pStats->memoryHeap[i]);
    3242 
    3243  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    3244  {
    3245  VmaMutexLock allocationsLock(m_AllocationsMutex[memTypeIndex]);
    3246  const uint32_t heapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
    3247  const VmaAllocationVector* const allocVector = m_pAllocations[memTypeIndex];
    3248  VMA_ASSERT(allocVector);
    3249  allocVector->AddStats(pStats, memTypeIndex, heapIndex);
    3250  }
    3251 
    3252  VmaPostprocessCalcStatInfo(pStats->total);
    3253  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
    3254  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
    3255  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
    3256  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
    3257 }
    3258 
    3259 bool VmaAllocator_T::FreeOwnMemory(const VkMappedMemoryRange* pMemory)
    3260 {
    3261  VkDeviceMemory vkMemory = VK_NULL_HANDLE;
    3262 
    3263  // Check all memory types because we don't know which one does pMemory come from.
    3264  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    3265  {
    3266  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex]);
    3267  OwnAllocationVectorType* const pOwnAllocations = m_pOwnAllocations[memTypeIndex];
    3268  VMA_ASSERT(pOwnAllocations);
    3269  VmaOwnAllocation* const pOwnAllocationsBeg = pOwnAllocations->data();
    3270  VmaOwnAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size();
    3271  VmaOwnAllocation* const pOwnAllocationIt = VmaBinaryFindFirstNotLess(
    3272  pOwnAllocationsBeg,
    3273  pOwnAllocationsEnd,
    3274  pMemory->memory,
    3275  VmaOwnAllocationMemoryHandleLess());
    3276  if((pOwnAllocationIt != pOwnAllocationsEnd) &&
    3277  (pOwnAllocationIt->m_hMemory == pMemory->memory))
    3278  {
    3279  VMA_ASSERT(pMemory->size == pOwnAllocationIt->m_Size && pMemory->offset == 0);
    3280  vkMemory = pOwnAllocationIt->m_hMemory;
    3281  const size_t ownAllocationIndex = pOwnAllocationIt - pOwnAllocationsBeg;
    3282  VectorRemove(*pOwnAllocations, ownAllocationIndex);
    3283  VMA_DEBUG_LOG(" Freed OwnMemory MemoryTypeIndex=%u", memTypeIndex);
    3284  break;
    3285  }
    3286  }
    3287 
    3288  // Found. Free VkDeviceMemory deferred until this point, outside of mutex lock,
    3289  // for performance reason.
    3290  if(vkMemory != VK_NULL_HANDLE)
    3291  {
    3292  vkFreeMemory(m_hDevice, vkMemory, GetAllocationCallbacks());
    3293  return true;
    3294  }
    3295  else
    3296  return false;
    3297 }
    3298 
    3299 #if VMA_STATS_STRING_ENABLED
    3300 
    3301 void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb)
    3302 {
    3303  bool ownAllocationsStarted = false;
    3304  for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    3305  {
    3306  VmaMutexLock ownAllocationsLock(m_OwnAllocationsMutex[memTypeIndex]);
    3307  OwnAllocationVectorType* const pOwnAllocVector = m_pOwnAllocations[memTypeIndex];
    3308  VMA_ASSERT(pOwnAllocVector);
    3309  if(pOwnAllocVector->empty() == false)
    3310  {
    3311  if(ownAllocationsStarted)
    3312  sb.Add(",\n\t\"Type ");
    3313  else
    3314  {
    3315  sb.Add(",\n\"OwnAllocations\": {\n\t\"Type ");
    3316  ownAllocationsStarted = true;
    3317  }
    3318  sb.AddNumber(memTypeIndex);
    3319  sb.Add("\": [");
    3320 
    3321  for(size_t i = 0; i < pOwnAllocVector->size(); ++i)
    3322  {
    3323  const VmaOwnAllocation& ownAlloc = (*pOwnAllocVector)[i];
    3324  if(i > 0)
    3325  sb.Add(",\n\t\t{ \"Size\": ");
    3326  else
    3327  sb.Add("\n\t\t{ \"Size\": ");
    3328  sb.AddNumber(ownAlloc.m_Size);
    3329  sb.Add(", \"Type\": ");
    3330  sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[ownAlloc.m_Type]);
    3331  sb.Add(" }");
    3332  }
    3333 
    3334  sb.Add("\n\t]");
    3335  }
    3336  }
    3337  if(ownAllocationsStarted)
    3338  sb.Add("\n}");
    3339 
    3340  {
    3341  bool allocationsStarted = false;
    3342  for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    3343  {
    3344  VmaMutexLock globalAllocationsLock(m_AllocationsMutex[memTypeIndex]);
    3345  if(m_pAllocations[memTypeIndex]->IsEmpty() == false)
    3346  {
    3347  if(allocationsStarted)
    3348  sb.Add(",\n\t\"Type ");
    3349  else
    3350  {
    3351  sb.Add(",\n\"Allocations\": {\n\t\"Type ");
    3352  allocationsStarted = true;
    3353  }
    3354  sb.AddNumber(memTypeIndex);
    3355  sb.Add("\": [");
    3356 
    3357  m_pAllocations[memTypeIndex]->PrintDetailedMap(sb);
    3358 
    3359  sb.Add("\n\t]");
    3360  }
    3361  }
    3362  if(allocationsStarted)
    3363  sb.Add("\n}");
    3364  }
    3365 }
    3366 
    3367 #endif // #if VMA_STATS_STRING_ENABLED
    3368 
    3369 static VkResult AllocateMemoryForImage(
    3370  VmaAllocator allocator,
    3371  VkImage image,
    3372  const VmaMemoryRequirements* pMemoryRequirements,
    3373  VmaSuballocationType suballocType,
    3374  VkMappedMemoryRange* pMemory,
    3375  uint32_t* pMemoryTypeIndex)
    3376 {
    3377  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements && pMemory);
    3378 
    3379  VkMemoryRequirements vkMemReq = {};
    3380  vkGetImageMemoryRequirements(allocator->m_hDevice, image, &vkMemReq);
    3381 
    3382  return allocator->AllocateMemory(
    3383  vkMemReq,
    3384  *pMemoryRequirements,
    3385  suballocType,
    3386  pMemory,
    3387  pMemoryTypeIndex);
    3388 }
    3389 
    3391 // Public interface
    3392 
    3393 VkResult vmaCreateAllocator(
    3394  const VmaAllocatorCreateInfo* pCreateInfo,
    3395  VmaAllocator* pAllocator)
    3396 {
    3397  VMA_ASSERT(pCreateInfo && pAllocator);
    3398  VMA_DEBUG_LOG("vmaCreateAllocator");
    3399  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
    3400  return VK_SUCCESS;
    3401 }
    3402 
    3403 void vmaDestroyAllocator(
    3404  VmaAllocator allocator)
    3405 {
    3406  if(allocator != VK_NULL_HANDLE)
    3407  {
    3408  VMA_DEBUG_LOG("vmaDestroyAllocator");
    3409  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
    3410  vma_delete(&allocationCallbacks, allocator);
    3411  }
    3412 }
    3413 
    3415  VmaAllocator allocator,
    3416  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
    3417 {
    3418  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
    3419  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
    3420 }
    3421 
    3423  VmaAllocator allocator,
    3424  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
    3425 {
    3426  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
    3427  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
    3428 }
    3429 
    3431  VmaAllocator allocator,
    3432  uint32_t memoryTypeIndex,
    3433  VkMemoryPropertyFlags* pFlags)
    3434 {
    3435  VMA_ASSERT(allocator && pFlags);
    3436  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
    3437  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
    3438 }
    3439 
    3440 void vmaCalculateStats(
    3441  VmaAllocator allocator,
    3442  VmaStats* pStats)
    3443 {
    3444  VMA_ASSERT(allocator && pStats);
    3445  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3446  allocator->CalculateStats(pStats);
    3447 }
    3448 
    3449 #if VMA_STATS_STRING_ENABLED
    3450 
    3451 void vmaBuildStatsString(
    3452  VmaAllocator allocator,
    3453  char** ppStatsString,
    3454  VkBool32 detailedMap)
    3455 {
    3456  VMA_ASSERT(allocator && ppStatsString);
    3457  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3458 
    3459  VmaStringBuilder sb(allocator);
    3460  {
    3461  VmaStats stats;
    3462  allocator->CalculateStats(&stats);
    3463 
    3464  sb.Add("{\n\"Total\": ");
    3465  VmaPrintStatInfo(sb, stats.total);
    3466 
    3467  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
    3468  {
    3469  sb.Add(",\n\"Heap ");
    3470  sb.AddNumber(heapIndex);
    3471  sb.Add("\": {\n\t\"Size\": ");
    3472  sb.AddNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
    3473  sb.Add(",\n\t\"Flags\": ");
    3474  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
    3475  sb.AddString("DEVICE_LOCAL");
    3476  else
    3477  sb.AddString("");
    3478  if(stats.memoryHeap[heapIndex].AllocationCount > 0)
    3479  {
    3480  sb.Add(",\n\t\"Stats:\": ");
    3481  VmaPrintStatInfo(sb, stats.memoryHeap[heapIndex]);
    3482  }
    3483 
    3484  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
    3485  {
    3486  if(allocator->m_MemProps.memoryTypes[typeIndex].heapIndex == heapIndex)
    3487  {
    3488  sb.Add(",\n\t\"Type ");
    3489  sb.AddNumber(typeIndex);
    3490  sb.Add("\": {\n\t\t\"Flags\": \"");
    3491  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
    3492  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
    3493  sb.Add(" DEVICE_LOCAL");
    3494  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
    3495  sb.Add(" HOST_VISIBLE");
    3496  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
    3497  sb.Add(" HOST_COHERENT");
    3498  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
    3499  sb.Add(" HOST_CACHED");
    3500  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
    3501  sb.Add(" LAZILY_ALLOCATED");
    3502  sb.Add("\"");
    3503  if(stats.memoryType[typeIndex].AllocationCount > 0)
    3504  {
    3505  sb.Add(",\n\t\t\"Stats\": ");
    3506  VmaPrintStatInfo(sb, stats.memoryType[typeIndex]);
    3507  }
    3508  sb.Add("\n\t}");
    3509  }
    3510  }
    3511  sb.Add("\n}");
    3512  }
    3513  if(detailedMap == VK_TRUE)
    3514  allocator->PrintDetailedMap(sb);
    3515  sb.Add("\n}\n");
    3516  }
    3517 
    3518  const size_t len = sb.GetLength();
    3519  char* const pChars = vma_new_array(allocator, char, len + 1);
    3520  if(len > 0)
    3521  memcpy(pChars, sb.GetData(), len);
    3522  pChars[len] = '\0';
    3523  *ppStatsString = pChars;
    3524 }
    3525 
    3526 void vmaFreeStatsString(
    3527  VmaAllocator allocator,
    3528  char* pStatsString)
    3529 {
    3530  if(pStatsString != VMA_NULL)
    3531  {
    3532  VMA_ASSERT(allocator);
    3533  size_t len = strlen(pStatsString);
    3534  vma_delete_array(allocator, pStatsString, len + 1);
    3535  }
    3536 }
    3537 
    3538 #endif // #if VMA_STATS_STRING_ENABLED
    3539 
    3542 VkResult vmaFindMemoryTypeIndex(
    3543  VmaAllocator allocator,
    3544  uint32_t memoryTypeBits,
    3545  const VmaMemoryRequirements* pMemoryRequirements,
    3546  uint32_t* pMemoryTypeIndex)
    3547 {
    3548  VMA_ASSERT(allocator != VK_NULL_HANDLE);
    3549  VMA_ASSERT(pMemoryRequirements != VMA_NULL);
    3550  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
    3551 
    3552  uint32_t requiredFlags = pMemoryRequirements->requiredFlags;
    3553  uint32_t preferredFlags = pMemoryRequirements->preferredFlags;
    3554  if(preferredFlags == 0)
    3555  preferredFlags = requiredFlags;
    3556  // preferredFlags, if not 0, must be subset of requiredFlags.
    3557  VMA_ASSERT((requiredFlags & ~preferredFlags) == 0);
    3558 
    3559  // Convert usage to requiredFlags and preferredFlags.
    3560  switch(pMemoryRequirements->usage)
    3561  {
    3563  break;
    3565  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    3566  break;
    3568  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
    3569  break;
    3571  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    3572  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    3573  break;
    3575  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    3576  preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
    3577  break;
    3578  default:
    3579  break;
    3580  }
    3581 
    3582  *pMemoryTypeIndex = UINT_MAX;
    3583  uint32_t minCost = UINT_MAX;
    3584  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
    3585  memTypeIndex < allocator->GetMemoryTypeCount();
    3586  ++memTypeIndex, memTypeBit <<= 1)
    3587  {
    3588  // This memory type is acceptable according to memoryTypeBits bitmask.
    3589  if((memTypeBit & memoryTypeBits) != 0)
    3590  {
    3591  const VkMemoryPropertyFlags currFlags =
    3592  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
    3593  // This memory type contains requiredFlags.
    3594  if((requiredFlags & ~currFlags) == 0)
    3595  {
    3596  // Calculate cost as number of bits from preferredFlags not present in this memory type.
    3597  uint32_t currCost = CountBitsSet(preferredFlags & ~currFlags);
    3598  // Remember memory type with lowest cost.
    3599  if(currCost < minCost)
    3600  {
    3601  *pMemoryTypeIndex = memTypeIndex;
    3602  if(currCost == 0)
    3603  return VK_SUCCESS;
    3604  minCost = currCost;
    3605  }
    3606  }
    3607  }
    3608  }
    3609  return (*pMemoryTypeIndex != UINT_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
    3610 }
    3611 
    3612 VkResult vmaAllocateMemory(
    3613  VmaAllocator allocator,
    3614  const VkMemoryRequirements* pVkMemoryRequirements,
    3615  const VmaMemoryRequirements* pVmaMemoryRequirements,
    3616  VkMappedMemoryRange* pMemory,
    3617  uint32_t* pMemoryTypeIndex)
    3618 {
    3619  VMA_ASSERT(allocator && pVkMemoryRequirements && pVmaMemoryRequirements && pMemory);
    3620 
    3621  VMA_DEBUG_LOG("vmaAllocateMemory");
    3622 
    3623  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3624 
    3625  return allocator->AllocateMemory(
    3626  *pVkMemoryRequirements,
    3627  *pVmaMemoryRequirements,
    3628  VMA_SUBALLOCATION_TYPE_UNKNOWN,
    3629  pMemory,
    3630  pMemoryTypeIndex);
    3631 }
    3632 
    3634  VmaAllocator allocator,
    3635  VkBuffer buffer,
    3636  const VmaMemoryRequirements* pMemoryRequirements,
    3637  VkMappedMemoryRange* pMemory,
    3638  uint32_t* pMemoryTypeIndex)
    3639 {
    3640  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pMemoryRequirements && pMemory);
    3641 
    3642  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
    3643 
    3644  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3645 
    3646  VkMemoryRequirements vkMemReq = {};
    3647  vkGetBufferMemoryRequirements(allocator->m_hDevice, buffer, &vkMemReq);
    3648 
    3649  return allocator->AllocateMemory(
    3650  vkMemReq,
    3651  *pMemoryRequirements,
    3652  VMA_SUBALLOCATION_TYPE_BUFFER,
    3653  pMemory,
    3654  pMemoryTypeIndex);
    3655 }
    3656 
    3657 VkResult vmaAllocateMemoryForImage(
    3658  VmaAllocator allocator,
    3659  VkImage image,
    3660  const VmaMemoryRequirements* pMemoryRequirements,
    3661  VkMappedMemoryRange* pMemory,
    3662  uint32_t* pMemoryTypeIndex)
    3663 {
    3664  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements);
    3665 
    3666  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
    3667 
    3668  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3669 
    3670  return AllocateMemoryForImage(
    3671  allocator,
    3672  image,
    3673  pMemoryRequirements,
    3674  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
    3675  pMemory,
    3676  pMemoryTypeIndex);
    3677 }
    3678 
    3679 void vmaFreeMemory(
    3680  VmaAllocator allocator,
    3681  const VkMappedMemoryRange* pMemory)
    3682 {
    3683  VMA_ASSERT(allocator && pMemory);
    3684 
    3685  VMA_DEBUG_LOG("vmaFreeMemory");
    3686 
    3687  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3688 
    3689  allocator->FreeMemory(pMemory);
    3690 }
    3691 
    3692 VkResult vmaMapMemory(
    3693  VmaAllocator allocator,
    3694  const VkMappedMemoryRange* pMemory,
    3695  void** ppData)
    3696 {
    3697  VMA_ASSERT(allocator && pMemory && ppData);
    3698 
    3699  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3700 
    3701  return vkMapMemory(allocator->m_hDevice, pMemory->memory,
    3702  pMemory->offset, pMemory->size, 0, ppData);
    3703 }
    3704 
    3705 void vmaUnmapMemory(
    3706  VmaAllocator allocator,
    3707  const VkMappedMemoryRange* pMemory)
    3708 {
    3709  VMA_ASSERT(allocator && pMemory);
    3710 
    3711  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3712 
    3713  vkUnmapMemory(allocator->m_hDevice, pMemory->memory);
    3714 }
    3715 
    3716 VkResult vmaCreateBuffer(
    3717  VmaAllocator allocator,
    3718  const VkBufferCreateInfo* pCreateInfo,
    3719  const VmaMemoryRequirements* pMemoryRequirements,
    3720  VkBuffer* pBuffer,
    3721  VkMappedMemoryRange* pMemory,
    3722  uint32_t* pMemoryTypeIndex)
    3723 {
    3724  VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements);
    3725 
    3726  VMA_DEBUG_LOG("vmaCreateBuffer");
    3727 
    3728  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3729 
    3730  // 1. Create VkBuffer.
    3731  VkResult res = vkCreateBuffer(allocator->m_hDevice, pCreateInfo, allocator->GetAllocationCallbacks(), pBuffer);
    3732  if(res >= 0)
    3733  {
    3734  VkMappedMemoryRange mem = {};
    3735 
    3736  // 2. vkGetBufferMemoryRequirements.
    3737  VkMemoryRequirements vkMemReq = {};
    3738  vkGetBufferMemoryRequirements(allocator->m_hDevice, *pBuffer, &vkMemReq);
    3739 
    3740  // 3. Allocate memory using allocator.
    3741  res = allocator->AllocateMemory(
    3742  vkMemReq,
    3743  *pMemoryRequirements,
    3744  VMA_SUBALLOCATION_TYPE_BUFFER,
    3745  &mem,
    3746  pMemoryTypeIndex);
    3747  if(res >= 0)
    3748  {
    3749  if(pMemory != VMA_NULL)
    3750  {
    3751  *pMemory = mem;
    3752  }
    3753  // 3. Bind buffer with memory.
    3754  res = vkBindBufferMemory(allocator->m_hDevice, *pBuffer, mem.memory, mem.offset);
    3755  if(res >= 0)
    3756  {
    3757  // All steps succeeded.
    3758  VmaMutexLock lock(allocator->m_BufferToMemoryMapMutex);
    3759  allocator->m_BufferToMemoryMap.insert(VmaPair<VkBuffer, VkMappedMemoryRange>(*pBuffer, mem));
    3760  return VK_SUCCESS;
    3761  }
    3762  allocator->FreeMemory(&mem);
    3763  return res;
    3764  }
    3765  vkDestroyBuffer(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
    3766  return res;
    3767  }
    3768  return res;
    3769 }
    3770 
    3771 void vmaDestroyBuffer(
    3772  VmaAllocator allocator,
    3773  VkBuffer buffer)
    3774 {
    3775  if(buffer != VK_NULL_HANDLE)
    3776  {
    3777  VMA_ASSERT(allocator);
    3778 
    3779  VMA_DEBUG_LOG("vmaDestroyBuffer");
    3780 
    3781  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3782 
    3783  VkMappedMemoryRange mem = {};
    3784  {
    3785  VmaMutexLock lock(allocator->m_BufferToMemoryMapMutex);
    3786  VMA_MAP_TYPE(VkBuffer, VkMappedMemoryRange)::iterator it = allocator->m_BufferToMemoryMap.find(buffer);
    3787  if(it == allocator->m_BufferToMemoryMap.end())
    3788  {
    3789  VMA_ASSERT(0 && "Trying to destroy buffer that was not created using vmaCreateBuffer or already freed.");
    3790  return;
    3791  }
    3792  mem = it->second;
    3793  allocator->m_BufferToMemoryMap.erase(it);
    3794  }
    3795 
    3796  vkDestroyBuffer(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
    3797 
    3798  allocator->FreeMemory(&mem);
    3799  }
    3800 }
    3801 
    3802 VkResult vmaCreateImage(
    3803  VmaAllocator allocator,
    3804  const VkImageCreateInfo* pCreateInfo,
    3805  const VmaMemoryRequirements* pMemoryRequirements,
    3806  VkImage* pImage,
    3807  VkMappedMemoryRange* pMemory,
    3808  uint32_t* pMemoryTypeIndex)
    3809 {
    3810  VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements);
    3811 
    3812  VMA_DEBUG_LOG("vmaCreateImage");
    3813 
    3814  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3815 
    3816  // 1. Create VkImage.
    3817  VkResult res = vkCreateImage(allocator->m_hDevice, pCreateInfo, allocator->GetAllocationCallbacks(), pImage);
    3818  if(res >= 0)
    3819  {
    3820  VkMappedMemoryRange mem = {};
    3821  VmaSuballocationType suballocType = pCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
    3822  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
    3823  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
    3824 
    3825  // 2. Allocate memory using allocator.
    3826  res = AllocateMemoryForImage(allocator, *pImage, pMemoryRequirements, suballocType, &mem, pMemoryTypeIndex);
    3827  if(res >= 0)
    3828  {
    3829  if(pMemory != VMA_NULL)
    3830  *pMemory = mem;
    3831  // 3. Bind image with memory.
    3832  res = vkBindImageMemory(allocator->m_hDevice, *pImage, mem.memory, mem.offset);
    3833  if(res >= 0)
    3834  {
    3835  // All steps succeeded.
    3836  VmaMutexLock lock(allocator->m_ImageToMemoryMapMutex);
    3837  allocator->m_ImageToMemoryMap.insert(VmaPair<VkImage, VkMappedMemoryRange>(*pImage, mem));
    3838  return VK_SUCCESS;
    3839  }
    3840  allocator->FreeMemory(&mem);
    3841  return res;
    3842  }
    3843  vkDestroyImage(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
    3844  return res;
    3845  }
    3846  return res;
    3847 }
    3848 
    3849 void vmaDestroyImage(
    3850  VmaAllocator allocator,
    3851  VkImage image)
    3852 {
    3853  if(image != VK_NULL_HANDLE)
    3854  {
    3855  VMA_ASSERT(allocator);
    3856 
    3857  VMA_DEBUG_LOG("vmaDestroyImage");
    3858 
    3859  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    3860 
    3861  VkMappedMemoryRange mem = {};
    3862  {
    3863  VmaMutexLock lock(allocator->m_ImageToMemoryMapMutex);
    3864  VMA_MAP_TYPE(VkImage, VkMappedMemoryRange)::iterator it = allocator->m_ImageToMemoryMap.find(image);
    3865  if(it == allocator->m_ImageToMemoryMap.end())
    3866  {
    3867  VMA_ASSERT(0 && "Trying to destroy buffer that was not created using vmaCreateBuffer or already freed.");
    3868  return;
    3869  }
    3870  mem = it->second;
    3871  allocator->m_ImageToMemoryMap.erase(it);
    3872  }
    3873 
    3874  vkDestroyImage(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
    3875 
    3876  allocator->FreeMemory(&mem);
    3877  }
    3878 }
    3879 
    3880 #endif // #ifdef VMA_IMPLEMENTATION
    3881 
    3882 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
    struct VmaMemoryRequirements VmaMemoryRequirements
    -
    void vmaUnmapMemory(VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
    -
    VkPhysicalDevice physicalDevice
    Vulkan physical device.
    Definition: vk_mem_alloc.h:169
    -
    VkResult vmaMapMemory(VmaAllocator allocator, const VkMappedMemoryRange *pMemory, void **ppData)
    -
    Memory will be used for writing on device and readback on host.
    Definition: vk_mem_alloc.h:280
    -
    VmaMemoryUsage usage
    Intended usage of memory.
    Definition: vk_mem_alloc.h:299
    -
    VkResult vmaAllocateMemoryForImage(VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
    Function similar to vmaAllocateMemoryForBuffer().
    -
    const VkAllocationCallbacks * pAllocationCallbacks
    Custom allocation callbacks.
    Definition: vk_mem_alloc.h:181
    -
    Description of a Allocator to be created.
    Definition: vk_mem_alloc.h:165
    -
    VkDeviceSize preferredSmallHeapBlockSize
    Size of a single memory block to allocate for resources from a small heap <= 512 MB.
    Definition: vk_mem_alloc.h:178
    -
    VmaStatInfo total
    Definition: vk_mem_alloc.h:236
    -
    VkDevice device
    Vulkan device.
    Definition: vk_mem_alloc.h:172
    -
    VkResult vmaAllocateMemory(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
    General purpose memory allocation.
    +Go to the documentation of this file.
    1 //
    2 // Copyright (c) 2017 Advanced Micro Devices, Inc. All rights reserved.
    3 //
    4 // Permission is hereby granted, free of charge, to any person obtaining a copy
    5 // of this software and associated documentation files (the "Software"), to deal
    6 // in the Software without restriction, including without limitation the rights
    7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    8 // copies of the Software, and to permit persons to whom the Software is
    9 // furnished to do so, subject to the following conditions:
    10 //
    11 // The above copyright notice and this permission notice shall be included in
    12 // all copies or substantial portions of the Software.
    13 //
    14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20 // THE SOFTWARE.
    21 //
    22 
    23 #ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H
    24 #define AMD_VULKAN_MEMORY_ALLOCATOR_H
    25 
    161 #include <vulkan/vulkan.h>
    162 
    164 
    168 VK_DEFINE_HANDLE(VmaAllocator)
    169 
    170 typedef void (VKAPI_PTR *PFN_vmaAllocateDeviceMemoryFunction)(
    172  VmaAllocator allocator,
    173  uint32_t memoryType,
    174  VkDeviceMemory memory,
    175  VkDeviceSize size);
    177 typedef void (VKAPI_PTR *PFN_vmaFreeDeviceMemoryFunction)(
    178  VmaAllocator allocator,
    179  uint32_t memoryType,
    180  VkDeviceMemory memory,
    181  VkDeviceSize size);
    182 
    188 typedef struct VmaDeviceMemoryCallbacks {
    194 
    196 typedef enum VmaAllocatorFlagBits {
    202 
    205 typedef VkFlags VmaAllocatorFlags;
    206 
    209 {
    213 
    214  VkPhysicalDevice physicalDevice;
    216 
    217  VkDevice device;
    219 
    222 
    225 
    226  const VkAllocationCallbacks* pAllocationCallbacks;
    228 
    231 
    233 VkResult vmaCreateAllocator(
    234  const VmaAllocatorCreateInfo* pCreateInfo,
    235  VmaAllocator* pAllocator);
    236 
    239  VmaAllocator allocator);
    240 
    246  VmaAllocator allocator,
    247  const VkPhysicalDeviceProperties** ppPhysicalDeviceProperties);
    248 
    254  VmaAllocator allocator,
    255  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties);
    256 
    264  VmaAllocator allocator,
    265  uint32_t memoryTypeIndex,
    266  VkMemoryPropertyFlags* pFlags);
    267 
    268 typedef struct VmaStatInfo
    269 {
    270  uint32_t AllocationCount;
    273  VkDeviceSize UsedBytes;
    274  VkDeviceSize UnusedBytes;
    275  VkDeviceSize SuballocationSizeMin, SuballocationSizeAvg, SuballocationSizeMax;
    276  VkDeviceSize UnusedRangeSizeMin, UnusedRangeSizeAvg, UnusedRangeSizeMax;
    277 } VmaStatInfo;
    278 
    280 struct VmaStats
    281 {
    282  VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES];
    283  VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS];
    285 };
    286 
    288 void vmaCalculateStats(
    289  VmaAllocator allocator,
    290  VmaStats* pStats);
    291 
    292 #define VMA_STATS_STRING_ENABLED 1
    293 
    294 #if VMA_STATS_STRING_ENABLED
    295 
    297 
    300  VmaAllocator allocator,
    301  char** ppStatsString,
    302  VkBool32 detailedMap);
    303 
    304 void vmaFreeStatsString(
    305  VmaAllocator allocator,
    306  char* pStatsString);
    307 
    308 #endif // #if VMA_STATS_STRING_ENABLED
    309 
    312 
    317 typedef enum VmaMemoryUsage
    318 {
    324 
    327 
    330 
    334 
    346 
    362 
    366 
    367 typedef struct VmaMemoryRequirements
    368 {
    378  VkMemoryPropertyFlags requiredFlags;
    383  VkMemoryPropertyFlags preferredFlags;
    385  void* pUserData;
    387 
    402 VkResult vmaFindMemoryTypeIndex(
    403  VmaAllocator allocator,
    404  uint32_t memoryTypeBits,
    405  const VmaMemoryRequirements* pMemoryRequirements,
    406  uint32_t* pMemoryTypeIndex);
    407 
    410 
    415 VK_DEFINE_HANDLE(VmaAllocation)
    416 
    417 
    419 typedef struct VmaAllocationInfo {
    424  uint32_t memoryType;
    431  VkDeviceMemory deviceMemory;
    436  VkDeviceSize offset;
    441  VkDeviceSize size;
    447  void* pMappedData;
    452  void* pUserData;
    454 
    465 VkResult vmaAllocateMemory(
    466  VmaAllocator allocator,
    467  const VkMemoryRequirements* pVkMemoryRequirements,
    468  const VmaMemoryRequirements* pVmaMemoryRequirements,
    469  VmaAllocation* pAllocation,
    470  VmaAllocationInfo* pAllocationInfo);
    471 
    479  VmaAllocator allocator,
    480  VkBuffer buffer,
    481  const VmaMemoryRequirements* pMemoryRequirements,
    482  VmaAllocation* pAllocation,
    483  VmaAllocationInfo* pAllocationInfo);
    484 
    487  VmaAllocator allocator,
    488  VkImage image,
    489  const VmaMemoryRequirements* pMemoryRequirements,
    490  VmaAllocation* pAllocation,
    491  VmaAllocationInfo* pAllocationInfo);
    492 
    494 void vmaFreeMemory(
    495  VmaAllocator allocator,
    496  VmaAllocation allocation);
    497 
    500  VmaAllocator allocator,
    501  VmaAllocation allocation,
    502  VmaAllocationInfo* pAllocationInfo);
    503 
    506  VmaAllocator allocator,
    507  VmaAllocation allocation,
    508  void* pUserData);
    509 
    518 VkResult vmaMapMemory(
    519  VmaAllocator allocator,
    520  VmaAllocation allocation,
    521  void** ppData);
    522 
    523 void vmaUnmapMemory(
    524  VmaAllocator allocator,
    525  VmaAllocation allocation);
    526 
    539 void vmaUnmapPersistentlyMappedMemory(VmaAllocator allocator);
    540 
    548 VkResult vmaMapPersistentlyMappedMemory(VmaAllocator allocator);
    549 
    551 typedef struct VmaDefragmentationInfo {
    556  VkDeviceSize maxBytesToMove;
    563 
    565 typedef struct VmaDefragmentationStats {
    567  VkDeviceSize bytesMoved;
    569  VkDeviceSize bytesFreed;
    575 
    646 VkResult vmaDefragment(
    647  VmaAllocator allocator,
    648  VmaAllocation* pAllocations,
    649  size_t allocationCount,
    650  VkBool32* pAllocationsChanged,
    651  const VmaDefragmentationInfo *pDefragmentationInfo,
    652  VmaDefragmentationStats* pDefragmentationStats);
    653 
    656 
    676 VkResult vmaCreateBuffer(
    677  VmaAllocator allocator,
    678  const VkBufferCreateInfo* pCreateInfo,
    679  const VmaMemoryRequirements* pMemoryRequirements,
    680  VkBuffer* pBuffer,
    681  VmaAllocation* pAllocation,
    682  VmaAllocationInfo* pAllocationInfo);
    683 
    684 void vmaDestroyBuffer(
    685  VmaAllocator allocator,
    686  VkBuffer buffer,
    687  VmaAllocation allocation);
    688 
    690 VkResult vmaCreateImage(
    691  VmaAllocator allocator,
    692  const VkImageCreateInfo* pCreateInfo,
    693  const VmaMemoryRequirements* pMemoryRequirements,
    694  VkImage* pImage,
    695  VmaAllocation* pAllocation,
    696  VmaAllocationInfo* pAllocationInfo);
    697 
    698 void vmaDestroyImage(
    699  VmaAllocator allocator,
    700  VkImage image,
    701  VmaAllocation allocation);
    702 
    705 #endif // AMD_VULKAN_MEMORY_ALLOCATOR_H
    706 
    707 #ifdef VMA_IMPLEMENTATION
    708 #undef VMA_IMPLEMENTATION
    709 
    710 #include <cstdint>
    711 #include <cstdlib>
    712 
    713 /*******************************************************************************
    714 CONFIGURATION SECTION
    715 
    716 Define some of these macros before each #include of this header or change them
    717 here if you need other then default behavior depending on your environment.
    718 */
    719 
    720 // Define this macro to 1 to make the library use STL containers instead of its own implementation.
    721 //#define VMA_USE_STL_CONTAINERS 1
    722 
    723 /* Set this macro to 1 to make the library including and using STL containers:
    724 std::pair, std::vector, std::list, std::unordered_map.
    725 
    726 Set it to 0 or undefined to make the library using its own implementation of
    727 the containers.
    728 */
    729 #if VMA_USE_STL_CONTAINERS
    730  #define VMA_USE_STL_VECTOR 1
    731  #define VMA_USE_STL_UNORDERED_MAP 1
    732  #define VMA_USE_STL_LIST 1
    733 #endif
    734 
    735 #if VMA_USE_STL_VECTOR
    736  #include <vector>
    737 #endif
    738 
    739 #if VMA_USE_STL_UNORDERED_MAP
    740  #include <unordered_map>
    741 #endif
    742 
    743 #if VMA_USE_STL_LIST
    744  #include <list>
    745 #endif
    746 
    747 /*
    748 Following headers are used in this CONFIGURATION section only, so feel free to
    749 remove them if not needed.
    750 */
    751 #include <cassert> // for assert
    752 #include <algorithm> // for min, max
    753 #include <mutex> // for std::mutex
    754 
    755 #if !defined(_WIN32)
    756  #include <malloc.h> // for aligned_alloc()
    757 #endif
    758 
    759 // Normal assert to check for programmer's errors, especially in Debug configuration.
    760 #ifndef VMA_ASSERT
    761  #ifdef _DEBUG
    762  #define VMA_ASSERT(expr) assert(expr)
    763  #else
    764  #define VMA_ASSERT(expr)
    765  #endif
    766 #endif
    767 
    768 // Assert that will be called very often, like inside data structures e.g. operator[].
    769 // Making it non-empty can make program slow.
    770 #ifndef VMA_HEAVY_ASSERT
    771  #ifdef _DEBUG
    772  #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr)
    773  #else
    774  #define VMA_HEAVY_ASSERT(expr)
    775  #endif
    776 #endif
    777 
    778 #ifndef VMA_NULL
    779  // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0.
    780  #define VMA_NULL nullptr
    781 #endif
    782 
    783 #ifndef VMA_ALIGN_OF
    784  #define VMA_ALIGN_OF(type) (__alignof(type))
    785 #endif
    786 
    787 #ifndef VMA_SYSTEM_ALIGNED_MALLOC
    788  #if defined(_WIN32)
    789  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (_aligned_malloc((size), (alignment)))
    790  #else
    791  #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) (aligned_alloc((alignment), (size) ))
    792  #endif
    793 #endif
    794 
    795 #ifndef VMA_SYSTEM_FREE
    796  #if defined(_WIN32)
    797  #define VMA_SYSTEM_FREE(ptr) _aligned_free(ptr)
    798  #else
    799  #define VMA_SYSTEM_FREE(ptr) free(ptr)
    800  #endif
    801 #endif
    802 
    803 #ifndef VMA_MIN
    804  #define VMA_MIN(v1, v2) (std::min((v1), (v2)))
    805 #endif
    806 
    807 #ifndef VMA_MAX
    808  #define VMA_MAX(v1, v2) (std::max((v1), (v2)))
    809 #endif
    810 
    811 #ifndef VMA_SWAP
    812  #define VMA_SWAP(v1, v2) std::swap((v1), (v2))
    813 #endif
    814 
    815 #ifndef VMA_SORT
    816  #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp)
    817 #endif
    818 
    819 #ifndef VMA_DEBUG_LOG
    820  #define VMA_DEBUG_LOG(format, ...)
    821  /*
    822  #define VMA_DEBUG_LOG(format, ...) do { \
    823  printf(format, __VA_ARGS__); \
    824  printf("\n"); \
    825  } while(false)
    826  */
    827 #endif
    828 
    829 // Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString.
    830 #if VMA_STATS_STRING_ENABLED
    831  static inline void VmaUint32ToStr(char* outStr, size_t strLen, uint32_t num)
    832  {
    833  _ultoa_s(num, outStr, strLen, 10);
    834  }
    835  static inline void VmaUint64ToStr(char* outStr, size_t strLen, uint64_t num)
    836  {
    837  _ui64toa_s(num, outStr, strLen, 10);
    838  }
    839 #endif
    840 
    841 #ifndef VMA_MUTEX
    842  class VmaMutex
    843  {
    844  public:
    845  VmaMutex() { }
    846  ~VmaMutex() { }
    847  void Lock() { m_Mutex.lock(); }
    848  void Unlock() { m_Mutex.unlock(); }
    849  private:
    850  std::mutex m_Mutex;
    851  };
    852  #define VMA_MUTEX VmaMutex
    853 #endif
    854 
    855 #ifndef VMA_BEST_FIT
    856 
    868  #define VMA_BEST_FIT (1)
    869 #endif
    870 
    871 #ifndef VMA_DEBUG_ALWAYS_OWN_MEMORY
    872 
    876  #define VMA_DEBUG_ALWAYS_OWN_MEMORY (0)
    877 #endif
    878 
    879 #ifndef VMA_DEBUG_ALIGNMENT
    880 
    884  #define VMA_DEBUG_ALIGNMENT (1)
    885 #endif
    886 
    887 #ifndef VMA_DEBUG_MARGIN
    888 
    892  #define VMA_DEBUG_MARGIN (0)
    893 #endif
    894 
    895 #ifndef VMA_DEBUG_GLOBAL_MUTEX
    896 
    900  #define VMA_DEBUG_GLOBAL_MUTEX (0)
    901 #endif
    902 
    903 #ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY
    904 
    908  #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1)
    909 #endif
    910 
    911 #ifndef VMA_SMALL_HEAP_MAX_SIZE
    912  #define VMA_SMALL_HEAP_MAX_SIZE (512 * 1024 * 1024)
    914 #endif
    915 
    916 #ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE
    917  #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256 * 1024 * 1024)
    919 #endif
    920 
    921 #ifndef VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE
    922  #define VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE (64 * 1024 * 1024)
    924 #endif
    925 
    926 /*******************************************************************************
    927 END OF CONFIGURATION
    928 */
    929 
    930 static VkAllocationCallbacks VmaEmptyAllocationCallbacks = {
    931  VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL };
    932 
    933 // Returns number of bits set to 1 in (v).
    934 static inline uint32_t CountBitsSet(uint32_t v)
    935 {
    936  uint32_t c = v - ((v >> 1) & 0x55555555);
    937  c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
    938  c = ((c >> 4) + c) & 0x0F0F0F0F;
    939  c = ((c >> 8) + c) & 0x00FF00FF;
    940  c = ((c >> 16) + c) & 0x0000FFFF;
    941  return c;
    942 }
    943 
    944 // Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16.
    945 // Use types like uint32_t, uint64_t as T.
    946 template <typename T>
    947 static inline T VmaAlignUp(T val, T align)
    948 {
    949  return (val + align - 1) / align * align;
    950 }
    951 
    952 // Division with mathematical rounding to nearest number.
    953 template <typename T>
    954 inline T VmaRoundDiv(T x, T y)
    955 {
    956  return (x + (y / (T)2)) / y;
    957 }
    958 
    959 #ifndef VMA_SORT
    960 
    961 template<typename Iterator, typename Compare>
    962 Iterator VmaQuickSortPartition(Iterator beg, Iterator end, Compare cmp)
    963 {
    964  Iterator centerValue = end; --centerValue;
    965  Iterator insertIndex = beg;
    966  for(Iterator i = beg; i < centerValue; ++i)
    967  {
    968  if(cmp(*i, *centerValue))
    969  {
    970  if(insertIndex != i)
    971  {
    972  VMA_SWAP(*i, *insertIndex);
    973  }
    974  ++insertIndex;
    975  }
    976  }
    977  if(insertIndex != centerValue)
    978  {
    979  VMA_SWAP(*insertIndex, *centerValue);
    980  }
    981  return insertIndex;
    982 }
    983 
    984 template<typename Iterator, typename Compare>
    985 void VmaQuickSort(Iterator beg, Iterator end, Compare cmp)
    986 {
    987  if(beg < end)
    988  {
    989  Iterator it = VmaQuickSortPartition<Iterator, Compare>(beg, end, cmp);
    990  VmaQuickSort<Iterator, Compare>(beg, it, cmp);
    991  VmaQuickSort<Iterator, Compare>(it + 1, end, cmp);
    992  }
    993 }
    994 
    995 #define VMA_SORT(beg, end, cmp) VmaQuickSort(beg, end, cmp)
    996 
    997 #endif // #ifndef VMA_SORT
    998 
    999 /*
    1000 Returns true if two memory blocks occupy overlapping pages.
    1001 ResourceA must be in less memory offset than ResourceB.
    1002 
    1003 Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)"
    1004 chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity".
    1005 */
    1006 static inline bool VmaBlocksOnSamePage(
    1007  VkDeviceSize resourceAOffset,
    1008  VkDeviceSize resourceASize,
    1009  VkDeviceSize resourceBOffset,
    1010  VkDeviceSize pageSize)
    1011 {
    1012  VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0);
    1013  VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1;
    1014  VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1);
    1015  VkDeviceSize resourceBStart = resourceBOffset;
    1016  VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1);
    1017  return resourceAEndPage == resourceBStartPage;
    1018 }
    1019 
    1020 enum VmaSuballocationType
    1021 {
    1022  VMA_SUBALLOCATION_TYPE_FREE = 0,
    1023  VMA_SUBALLOCATION_TYPE_UNKNOWN = 1,
    1024  VMA_SUBALLOCATION_TYPE_BUFFER = 2,
    1025  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3,
    1026  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4,
    1027  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5,
    1028  VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF
    1029 };
    1030 
    1031 /*
    1032 Returns true if given suballocation types could conflict and must respect
    1033 VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer
    1034 or linear image and another one is optimal image. If type is unknown, behave
    1035 conservatively.
    1036 */
    1037 static inline bool VmaIsBufferImageGranularityConflict(
    1038  VmaSuballocationType suballocType1,
    1039  VmaSuballocationType suballocType2)
    1040 {
    1041  if(suballocType1 > suballocType2)
    1042  VMA_SWAP(suballocType1, suballocType2);
    1043 
    1044  switch(suballocType1)
    1045  {
    1046  case VMA_SUBALLOCATION_TYPE_FREE:
    1047  return false;
    1048  case VMA_SUBALLOCATION_TYPE_UNKNOWN:
    1049  return true;
    1050  case VMA_SUBALLOCATION_TYPE_BUFFER:
    1051  return
    1052  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
    1053  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    1054  case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN:
    1055  return
    1056  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN ||
    1057  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR ||
    1058  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    1059  case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR:
    1060  return
    1061  suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL;
    1062  case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL:
    1063  return false;
    1064  default:
    1065  VMA_ASSERT(0);
    1066  return true;
    1067  }
    1068 }
    1069 
    1070 // Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope).
    1071 struct VmaMutexLock
    1072 {
    1073 public:
    1074  VmaMutexLock(VMA_MUTEX& mutex, bool useMutex) :
    1075  m_pMutex(useMutex ? &mutex : VMA_NULL)
    1076  {
    1077  if(m_pMutex)
    1078  {
    1079  m_pMutex->Lock();
    1080  }
    1081  }
    1082 
    1083  ~VmaMutexLock()
    1084  {
    1085  if(m_pMutex)
    1086  {
    1087  m_pMutex->Unlock();
    1088  }
    1089  }
    1090 
    1091 private:
    1092  VMA_MUTEX* m_pMutex;
    1093 };
    1094 
    1095 #if VMA_DEBUG_GLOBAL_MUTEX
    1096  static VMA_MUTEX gDebugGlobalMutex;
    1097  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex);
    1098 #else
    1099  #define VMA_DEBUG_GLOBAL_MUTEX_LOCK
    1100 #endif
    1101 
    1102 // Minimum size of a free suballocation to register it in the free suballocation collection.
    1103 static const VkDeviceSize VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER = 16;
    1104 
    1105 /*
    1106 Performs binary search and returns iterator to first element that is greater or
    1107 equal to (key), according to comparison (cmp).
    1108 
    1109 Cmp should return true if first argument is less than second argument.
    1110 
    1111 Returned value is the found element, if present in the collection or place where
    1112 new element with value (key) should be inserted.
    1113 */
    1114 template <typename IterT, typename KeyT, typename CmpT>
    1115 static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT &key, CmpT cmp)
    1116 {
    1117  size_t down = 0, up = (end - beg);
    1118  while(down < up)
    1119  {
    1120  const size_t mid = (down + up) / 2;
    1121  if(cmp(*(beg+mid), key))
    1122  down = mid + 1;
    1123  else
    1124  up = mid;
    1125  }
    1126  return beg + down;
    1127 }
    1128 
    1130 // Memory allocation
    1131 
    1132 static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment)
    1133 {
    1134  if((pAllocationCallbacks != VMA_NULL) &&
    1135  (pAllocationCallbacks->pfnAllocation != VMA_NULL))
    1136  {
    1137  return (*pAllocationCallbacks->pfnAllocation)(
    1138  pAllocationCallbacks->pUserData,
    1139  size,
    1140  alignment,
    1141  VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
    1142  }
    1143  else
    1144  {
    1145  return VMA_SYSTEM_ALIGNED_MALLOC(size, alignment);
    1146  }
    1147 }
    1148 
    1149 static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr)
    1150 {
    1151  if((pAllocationCallbacks != VMA_NULL) &&
    1152  (pAllocationCallbacks->pfnFree != VMA_NULL))
    1153  {
    1154  (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr);
    1155  }
    1156  else
    1157  {
    1158  VMA_SYSTEM_FREE(ptr);
    1159  }
    1160 }
    1161 
    1162 template<typename T>
    1163 static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
    1164 {
    1165  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
    1166 }
    1167 
    1168 template<typename T>
    1169 static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
    1170 {
    1171  return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
    1172 }
    1173 
    1174 #define vma_new(allocator, type) new(VmaAllocate<type>(allocator))(type)
    1175 
    1176 #define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
    1177 
    1178 template<typename T>
    1179 static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
    1180 {
    1181  ptr->~T();
    1182  VmaFree(pAllocationCallbacks, ptr);
    1183 }
    1184 
    1185 template<typename T>
    1186 static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
    1187 {
    1188  if(ptr != VMA_NULL)
    1189  {
    1190  for(size_t i = count; i--; )
    1191  ptr[i].~T();
    1192  VmaFree(pAllocationCallbacks, ptr);
    1193  }
    1194 }
    1195 
    1196 // STL-compatible allocator.
    1197 template<typename T>
    1198 class VmaStlAllocator
    1199 {
    1200 public:
    1201  const VkAllocationCallbacks* const m_pCallbacks;
    1202  typedef T value_type;
    1203 
    1204  VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) { }
    1205  template<typename U> VmaStlAllocator(const VmaStlAllocator<U>& src) : m_pCallbacks(src.m_pCallbacks) { }
    1206 
    1207  T* allocate(size_t n) { return VmaAllocateArray<T>(m_pCallbacks, n); }
    1208  void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); }
    1209 
    1210  template<typename U>
    1211  bool operator==(const VmaStlAllocator<U>& rhs) const
    1212  {
    1213  return m_pCallbacks == rhs.m_pCallbacks;
    1214  }
    1215  template<typename U>
    1216  bool operator!=(const VmaStlAllocator<U>& rhs) const
    1217  {
    1218  return m_pCallbacks != rhs.m_pCallbacks;
    1219  }
    1220 
    1221  VmaStlAllocator& operator=(const VmaStlAllocator& x) = delete;
    1222 };
    1223 
    1224 #if VMA_USE_STL_VECTOR
    1225 
    1226 #define VmaVector std::vector
    1227 
    1228 template<typename T, typename allocatorT>
    1229 static void VectorInsert(std::vector<T, allocatorT>& vec, size_t index, const T& item)
    1230 {
    1231  vec.insert(vec.begin() + index, item);
    1232 }
    1233 
    1234 template<typename T, typename allocatorT>
    1235 static void VectorRemove(std::vector<T, allocatorT>& vec, size_t index)
    1236 {
    1237  vec.erase(vec.begin() + index);
    1238 }
    1239 
    1240 #else // #if VMA_USE_STL_VECTOR
    1241 
    1242 /* Class with interface compatible with subset of std::vector.
    1243 T must be POD because constructors and destructors are not called and memcpy is
    1244 used for these objects. */
    1245 template<typename T, typename AllocatorT>
    1246 class VmaVector
    1247 {
    1248 public:
    1249  VmaVector(const AllocatorT& allocator) :
    1250  m_Allocator(allocator),
    1251  m_pArray(VMA_NULL),
    1252  m_Count(0),
    1253  m_Capacity(0)
    1254  {
    1255  }
    1256 
    1257  VmaVector(size_t count, const AllocatorT& allocator) :
    1258  m_Allocator(allocator),
    1259  m_pArray(count ? (T*)VmaAllocateArray<T>(allocator->m_pCallbacks, count) : VMA_NULL),
    1260  m_Count(count),
    1261  m_Capacity(count)
    1262  {
    1263  }
    1264 
    1265  VmaVector(const VmaVector<T, AllocatorT>& src) :
    1266  m_Allocator(src.m_Allocator),
    1267  m_pArray(src.m_Count ? (T*)VmaAllocateArray<T>(src->m_pCallbacks, src.m_Count) : VMA_NULL),
    1268  m_Count(src.m_Count),
    1269  m_Capacity(src.m_Count)
    1270  {
    1271  if(m_Count != 0)
    1272  memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T));
    1273  }
    1274 
    1275  ~VmaVector()
    1276  {
    1277  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    1278  }
    1279 
    1280  VmaVector& operator=(const VmaVector<T, AllocatorT>& rhs)
    1281  {
    1282  if(&rhs != this)
    1283  {
    1284  Resize(rhs.m_Count);
    1285  if(m_Count != 0)
    1286  memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T));
    1287  }
    1288  return *this;
    1289  }
    1290 
    1291  bool empty() const { return m_Count == 0; }
    1292  size_t size() const { return m_Count; }
    1293  T* data() { return m_pArray; }
    1294  const T* data() const { return m_pArray; }
    1295 
    1296  T& operator[](size_t index)
    1297  {
    1298  VMA_HEAVY_ASSERT(index < m_Count);
    1299  return m_pArray[index];
    1300  }
    1301  const T& operator[](size_t index) const
    1302  {
    1303  VMA_HEAVY_ASSERT(index < m_Count);
    1304  return m_pArray[index];
    1305  }
    1306 
    1307  T& front()
    1308  {
    1309  VMA_HEAVY_ASSERT(m_Count > 0);
    1310  return m_pArray[0];
    1311  }
    1312  const T& front() const
    1313  {
    1314  VMA_HEAVY_ASSERT(m_Count > 0);
    1315  return m_pArray[0];
    1316  }
    1317  T& back()
    1318  {
    1319  VMA_HEAVY_ASSERT(m_Count > 0);
    1320  return m_pArray[m_Count - 1];
    1321  }
    1322  const T& back() const
    1323  {
    1324  VMA_HEAVY_ASSERT(m_Count > 0);
    1325  return m_pArray[m_Count - 1];
    1326  }
    1327 
    1328  void reserve(size_t newCapacity, bool freeMemory = false)
    1329  {
    1330  newCapacity = VMA_MAX(newCapacity, m_Count);
    1331 
    1332  if((newCapacity < m_Capacity) && !freeMemory)
    1333  newCapacity = m_Capacity;
    1334 
    1335  if(newCapacity != m_Capacity)
    1336  {
    1337  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator, newCapacity) : VMA_NULL;
    1338  if(m_Count != 0)
    1339  memcpy(newArray, m_pArray, m_Count * sizeof(T));
    1340  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    1341  m_Capacity = newCapacity;
    1342  m_pArray = newArray;
    1343  }
    1344  }
    1345 
    1346  void resize(size_t newCount, bool freeMemory = false)
    1347  {
    1348  size_t newCapacity = m_Capacity;
    1349  if(newCount > m_Capacity)
    1350  newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8));
    1351  else if(freeMemory)
    1352  newCapacity = newCount;
    1353 
    1354  if(newCapacity != m_Capacity)
    1355  {
    1356  T* const newArray = newCapacity ? VmaAllocateArray<T>(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL;
    1357  const size_t elementsToCopy = VMA_MIN(m_Count, newCount);
    1358  if(elementsToCopy != 0)
    1359  memcpy(newArray, m_pArray, elementsToCopy * sizeof(T));
    1360  VmaFree(m_Allocator.m_pCallbacks, m_pArray);
    1361  m_Capacity = newCapacity;
    1362  m_pArray = newArray;
    1363  }
    1364 
    1365  m_Count = newCount;
    1366  }
    1367 
    1368  void clear(bool freeMemory = false)
    1369  {
    1370  resize(0, freeMemory);
    1371  }
    1372 
    1373  void insert(size_t index, const T& src)
    1374  {
    1375  VMA_HEAVY_ASSERT(index <= m_Count);
    1376  const size_t oldCount = size();
    1377  resize(oldCount + 1);
    1378  if(index < oldCount)
    1379  memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T));
    1380  m_pArray[index] = src;
    1381  }
    1382 
    1383  void remove(size_t index)
    1384  {
    1385  VMA_HEAVY_ASSERT(index < m_Count);
    1386  const size_t oldCount = size();
    1387  if(index < oldCount - 1)
    1388  memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T));
    1389  resize(oldCount - 1);
    1390  }
    1391 
    1392  void push_back(const T& src)
    1393  {
    1394  const size_t newIndex = size();
    1395  resize(newIndex + 1);
    1396  m_pArray[newIndex] = src;
    1397  }
    1398 
    1399  void pop_back()
    1400  {
    1401  VMA_HEAVY_ASSERT(m_Count > 0);
    1402  resize(size() - 1);
    1403  }
    1404 
    1405  void push_front(const T& src)
    1406  {
    1407  insert(0, src);
    1408  }
    1409 
    1410  void pop_front()
    1411  {
    1412  VMA_HEAVY_ASSERT(m_Count > 0);
    1413  remove(0);
    1414  }
    1415 
    1416  typedef T* iterator;
    1417 
    1418  iterator begin() { return m_pArray; }
    1419  iterator end() { return m_pArray + m_Count; }
    1420 
    1421 private:
    1422  AllocatorT m_Allocator;
    1423  T* m_pArray;
    1424  size_t m_Count;
    1425  size_t m_Capacity;
    1426 };
    1427 
    1428 template<typename T, typename allocatorT>
    1429 static void VectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
    1430 {
    1431  vec.insert(index, item);
    1432 }
    1433 
    1434 template<typename T, typename allocatorT>
    1435 static void VectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
    1436 {
    1437  vec.remove(index);
    1438 }
    1439 
    1440 #endif // #if VMA_USE_STL_VECTOR
    1441 
    1443 // class VmaPoolAllocator
    1444 
    1445 /*
    1446 Allocator for objects of type T using a list of arrays (pools) to speed up
    1447 allocation. Number of elements that can be allocated is not bounded because
    1448 allocator can create multiple blocks.
    1449 */
    1450 template<typename T>
    1451 class VmaPoolAllocator
    1452 {
    1453 public:
    1454  VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock);
    1455  ~VmaPoolAllocator();
    1456  void Clear();
    1457  T* Alloc();
    1458  void Free(T* ptr);
    1459 
    1460 private:
    1461  union Item
    1462  {
    1463  uint32_t NextFreeIndex;
    1464  T Value;
    1465  };
    1466 
    1467  struct ItemBlock
    1468  {
    1469  Item* pItems;
    1470  uint32_t FirstFreeIndex;
    1471  };
    1472 
    1473  const VkAllocationCallbacks* m_pAllocationCallbacks;
    1474  size_t m_ItemsPerBlock;
    1475  VmaVector< ItemBlock, VmaStlAllocator<ItemBlock> > m_ItemBlocks;
    1476 
    1477  ItemBlock& CreateNewBlock();
    1478 };
    1479 
    1480 template<typename T>
    1481 VmaPoolAllocator<T>::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, size_t itemsPerBlock) :
    1482  m_pAllocationCallbacks(pAllocationCallbacks),
    1483  m_ItemsPerBlock(itemsPerBlock),
    1484  m_ItemBlocks(VmaStlAllocator<ItemBlock>(pAllocationCallbacks))
    1485 {
    1486  VMA_ASSERT(itemsPerBlock > 0);
    1487 }
    1488 
    1489 template<typename T>
    1490 VmaPoolAllocator<T>::~VmaPoolAllocator()
    1491 {
    1492  Clear();
    1493 }
    1494 
    1495 template<typename T>
    1496 void VmaPoolAllocator<T>::Clear()
    1497 {
    1498  for(size_t i = m_ItemBlocks.size(); i--; )
    1499  vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemsPerBlock);
    1500  m_ItemBlocks.clear();
    1501 }
    1502 
    1503 template<typename T>
    1504 T* VmaPoolAllocator<T>::Alloc()
    1505 {
    1506  for(size_t i = m_ItemBlocks.size(); i--; )
    1507  {
    1508  ItemBlock& block = m_ItemBlocks[i];
    1509  // This block has some free items: Use first one.
    1510  if(block.FirstFreeIndex != UINT32_MAX)
    1511  {
    1512  Item* const pItem = &block.pItems[block.FirstFreeIndex];
    1513  block.FirstFreeIndex = pItem->NextFreeIndex;
    1514  return &pItem->Value;
    1515  }
    1516  }
    1517 
    1518  // No block has free item: Create new one and use it.
    1519  ItemBlock& newBlock = CreateNewBlock();
    1520  Item* const pItem = &newBlock.pItems[0];
    1521  newBlock.FirstFreeIndex = pItem->NextFreeIndex;
    1522  return &pItem->Value;
    1523 }
    1524 
    1525 template<typename T>
    1526 void VmaPoolAllocator<T>::Free(T* ptr)
    1527 {
    1528  // Search all memory blocks to find ptr.
    1529  for(size_t i = 0; i < m_ItemBlocks.size(); ++i)
    1530  {
    1531  ItemBlock& block = m_ItemBlocks[i];
    1532 
    1533  // Casting to union.
    1534  Item* pItemPtr;
    1535  memcpy(&pItemPtr, &ptr, sizeof(pItemPtr));
    1536 
    1537  // Check if pItemPtr is in address range of this block.
    1538  if((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + m_ItemsPerBlock))
    1539  {
    1540  const uint32_t index = static_cast<uint32_t>(pItemPtr - block.pItems);
    1541  pItemPtr->NextFreeIndex = block.FirstFreeIndex;
    1542  block.FirstFreeIndex = index;
    1543  return;
    1544  }
    1545  }
    1546  VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool.");
    1547 }
    1548 
    1549 template<typename T>
    1550 typename VmaPoolAllocator<T>::ItemBlock& VmaPoolAllocator<T>::CreateNewBlock()
    1551 {
    1552  ItemBlock newBlock = {
    1553  vma_new_array(m_pAllocationCallbacks, Item, m_ItemsPerBlock), 0 };
    1554 
    1555  m_ItemBlocks.push_back(newBlock);
    1556 
    1557  // Setup singly-linked list of all free items in this block.
    1558  for(uint32_t i = 0; i < m_ItemsPerBlock - 1; ++i)
    1559  newBlock.pItems[i].NextFreeIndex = i + 1;
    1560  newBlock.pItems[m_ItemsPerBlock - 1].NextFreeIndex = UINT32_MAX;
    1561  return m_ItemBlocks.back();
    1562 }
    1563 
    1565 // class VmaRawList, VmaList
    1566 
    1567 #if VMA_USE_STL_LIST
    1568 
    1569 #define VmaList std::list
    1570 
    1571 #else // #if VMA_USE_STL_LIST
    1572 
    1573 template<typename T>
    1574 struct VmaListItem
    1575 {
    1576  VmaListItem* pPrev;
    1577  VmaListItem* pNext;
    1578  T Value;
    1579 };
    1580 
    1581 // Doubly linked list.
    1582 template<typename T>
    1583 class VmaRawList
    1584 {
    1585 public:
    1586  typedef VmaListItem<T> ItemType;
    1587 
    1588  VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks);
    1589  ~VmaRawList();
    1590  void Clear();
    1591 
    1592  size_t GetCount() const { return m_Count; }
    1593  bool IsEmpty() const { return m_Count == 0; }
    1594 
    1595  ItemType* Front() { return m_pFront; }
    1596  const ItemType* Front() const { return m_pFront; }
    1597  ItemType* Back() { return m_pBack; }
    1598  const ItemType* Back() const { return m_pBack; }
    1599 
    1600  ItemType* PushBack();
    1601  ItemType* PushFront();
    1602  ItemType* PushBack(const T& value);
    1603  ItemType* PushFront(const T& value);
    1604  void PopBack();
    1605  void PopFront();
    1606 
    1607  // Item can be null - it means PushBack.
    1608  ItemType* InsertBefore(ItemType* pItem);
    1609  // Item can be null - it means PushFront.
    1610  ItemType* InsertAfter(ItemType* pItem);
    1611 
    1612  ItemType* InsertBefore(ItemType* pItem, const T& value);
    1613  ItemType* InsertAfter(ItemType* pItem, const T& value);
    1614 
    1615  void Remove(ItemType* pItem);
    1616 
    1617 private:
    1618  const VkAllocationCallbacks* const m_pAllocationCallbacks;
    1619  VmaPoolAllocator<ItemType> m_ItemAllocator;
    1620  ItemType* m_pFront;
    1621  ItemType* m_pBack;
    1622  size_t m_Count;
    1623 
    1624  // Declared not defined, to block copy constructor and assignment operator.
    1625  VmaRawList(const VmaRawList<T>& src);
    1626  VmaRawList<T>& operator=(const VmaRawList<T>& rhs);
    1627 };
    1628 
    1629 template<typename T>
    1630 VmaRawList<T>::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) :
    1631  m_pAllocationCallbacks(pAllocationCallbacks),
    1632  m_ItemAllocator(pAllocationCallbacks, 128),
    1633  m_pFront(VMA_NULL),
    1634  m_pBack(VMA_NULL),
    1635  m_Count(0)
    1636 {
    1637 }
    1638 
    1639 template<typename T>
    1640 VmaRawList<T>::~VmaRawList()
    1641 {
    1642  // Intentionally not calling Clear, because that would be unnecessary
    1643  // computations to return all items to m_ItemAllocator as free.
    1644 }
    1645 
    1646 template<typename T>
    1647 void VmaRawList<T>::Clear()
    1648 {
    1649  if(IsEmpty() == false)
    1650  {
    1651  ItemType* pItem = m_pBack;
    1652  while(pItem != VMA_NULL)
    1653  {
    1654  ItemType* const pPrevItem = pItem->pPrev;
    1655  m_ItemAllocator.Free(pItem);
    1656  pItem = pPrevItem;
    1657  }
    1658  m_pFront = VMA_NULL;
    1659  m_pBack = VMA_NULL;
    1660  m_Count = 0;
    1661  }
    1662 }
    1663 
    1664 template<typename T>
    1665 VmaListItem<T>* VmaRawList<T>::PushBack()
    1666 {
    1667  ItemType* const pNewItem = m_ItemAllocator.Alloc();
    1668  pNewItem->pNext = VMA_NULL;
    1669  if(IsEmpty())
    1670  {
    1671  pNewItem->pPrev = VMA_NULL;
    1672  m_pFront = pNewItem;
    1673  m_pBack = pNewItem;
    1674  m_Count = 1;
    1675  }
    1676  else
    1677  {
    1678  pNewItem->pPrev = m_pBack;
    1679  m_pBack->pNext = pNewItem;
    1680  m_pBack = pNewItem;
    1681  ++m_Count;
    1682  }
    1683  return pNewItem;
    1684 }
    1685 
    1686 template<typename T>
    1687 VmaListItem<T>* VmaRawList<T>::PushFront()
    1688 {
    1689  ItemType* const pNewItem = m_ItemAllocator.Alloc();
    1690  pNewItem->pPrev = VMA_NULL;
    1691  if(IsEmpty())
    1692  {
    1693  pNewItem->pNext = VMA_NULL;
    1694  m_pFront = pNewItem;
    1695  m_pBack = pNewItem;
    1696  m_Count = 1;
    1697  }
    1698  else
    1699  {
    1700  pNewItem->pNext = m_pFront;
    1701  m_pFront->pPrev = pNewItem;
    1702  m_pFront = pNewItem;
    1703  ++m_Count;
    1704  }
    1705  return pNewItem;
    1706 }
    1707 
    1708 template<typename T>
    1709 VmaListItem<T>* VmaRawList<T>::PushBack(const T& value)
    1710 {
    1711  ItemType* const pNewItem = PushBack();
    1712  pNewItem->Value = value;
    1713  return pNewItem;
    1714 }
    1715 
    1716 template<typename T>
    1717 VmaListItem<T>* VmaRawList<T>::PushFront(const T& value)
    1718 {
    1719  ItemType* const pNewItem = PushFront();
    1720  pNewItem->Value = value;
    1721  return pNewItem;
    1722 }
    1723 
    1724 template<typename T>
    1725 void VmaRawList<T>::PopBack()
    1726 {
    1727  VMA_HEAVY_ASSERT(m_Count > 0);
    1728  ItemType* const pBackItem = m_pBack;
    1729  ItemType* const pPrevItem = pBackItem->pPrev;
    1730  if(pPrevItem != VMA_NULL)
    1731  pPrevItem->pNext = VMA_NULL;
    1732  m_pBack = pPrevItem;
    1733  m_ItemAllocator.Free(pBackItem);
    1734  --m_Count;
    1735 }
    1736 
    1737 template<typename T>
    1738 void VmaRawList<T>::PopFront()
    1739 {
    1740  VMA_HEAVY_ASSERT(m_Count > 0);
    1741  ItemType* const pFrontItem = m_pFront;
    1742  ItemType* const pNextItem = pFrontItem->pNext;
    1743  if(pNextItem != VMA_NULL)
    1744  pNextItem->pPrev = VMA_NULL;
    1745  m_pFront = pNextItem;
    1746  m_ItemAllocator.Free(pFrontItem);
    1747  --m_Count;
    1748 }
    1749 
    1750 template<typename T>
    1751 void VmaRawList<T>::Remove(ItemType* pItem)
    1752 {
    1753  VMA_HEAVY_ASSERT(pItem != VMA_NULL);
    1754  VMA_HEAVY_ASSERT(m_Count > 0);
    1755 
    1756  if(pItem->pPrev != VMA_NULL)
    1757  pItem->pPrev->pNext = pItem->pNext;
    1758  else
    1759  {
    1760  VMA_HEAVY_ASSERT(m_pFront == pItem);
    1761  m_pFront = pItem->pNext;
    1762  }
    1763 
    1764  if(pItem->pNext != VMA_NULL)
    1765  pItem->pNext->pPrev = pItem->pPrev;
    1766  else
    1767  {
    1768  VMA_HEAVY_ASSERT(m_pBack == pItem);
    1769  m_pBack = pItem->pPrev;
    1770  }
    1771 
    1772  m_ItemAllocator.Free(pItem);
    1773  --m_Count;
    1774 }
    1775 
    1776 template<typename T>
    1777 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem)
    1778 {
    1779  if(pItem != VMA_NULL)
    1780  {
    1781  ItemType* const prevItem = pItem->pPrev;
    1782  ItemType* const newItem = m_ItemAllocator.Alloc();
    1783  newItem->pPrev = prevItem;
    1784  newItem->pNext = pItem;
    1785  pItem->pPrev = newItem;
    1786  if(prevItem != VMA_NULL)
    1787  prevItem->pNext = newItem;
    1788  else
    1789  {
    1790  VMA_HEAVY_ASSERT(m_pFront = pItem);
    1791  m_pFront = newItem;
    1792  }
    1793  ++m_Count;
    1794  return newItem;
    1795  }
    1796  else
    1797  return PushBack();
    1798 }
    1799 
    1800 template<typename T>
    1801 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem)
    1802 {
    1803  if(pItem != VMA_NULL)
    1804  {
    1805  ItemType* const nextItem = pItem->pNext;
    1806  ItemType* const newItem = m_ItemAllocator.Alloc();
    1807  newItem->pNext = nextItem;
    1808  newItem->pPrev = pItem;
    1809  pItem->pNext = newItem;
    1810  if(nextItem != VMA_NULL)
    1811  nextItem->pPrev = newItem;
    1812  else
    1813  {
    1814  VMA_HEAVY_ASSERT(m_pBack = pItem);
    1815  m_pBack = newItem;
    1816  }
    1817  ++m_Count;
    1818  return newItem;
    1819  }
    1820  else
    1821  return PushFront();
    1822 }
    1823 
    1824 template<typename T>
    1825 VmaListItem<T>* VmaRawList<T>::InsertBefore(ItemType* pItem, const T& value)
    1826 {
    1827  ItemType* const newItem = InsertBefore(pItem);
    1828  newItem->Value = value;
    1829  return newItem;
    1830 }
    1831 
    1832 template<typename T>
    1833 VmaListItem<T>* VmaRawList<T>::InsertAfter(ItemType* pItem, const T& value)
    1834 {
    1835  ItemType* const newItem = InsertAfter(pItem);
    1836  newItem->Value = value;
    1837  return newItem;
    1838 }
    1839 
    1840 template<typename T, typename AllocatorT>
    1841 class VmaList
    1842 {
    1843 public:
    1844  class iterator
    1845  {
    1846  public:
    1847  iterator() :
    1848  m_pList(VMA_NULL),
    1849  m_pItem(VMA_NULL)
    1850  {
    1851  }
    1852 
    1853  T& operator*() const
    1854  {
    1855  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1856  return m_pItem->Value;
    1857  }
    1858  T* operator->() const
    1859  {
    1860  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1861  return &m_pItem->Value;
    1862  }
    1863 
    1864  iterator& operator++()
    1865  {
    1866  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1867  m_pItem = m_pItem->pNext;
    1868  return *this;
    1869  }
    1870  iterator& operator--()
    1871  {
    1872  if(m_pItem != VMA_NULL)
    1873  m_pItem = m_pItem->pPrev;
    1874  else
    1875  {
    1876  VMA_HEAVY_ASSERT(!m_pList.IsEmpty());
    1877  m_pItem = m_pList->Back();
    1878  }
    1879  return *this;
    1880  }
    1881 
    1882  iterator operator++(int)
    1883  {
    1884  iterator result = *this;
    1885  ++*this;
    1886  return result;
    1887  }
    1888  iterator operator--(int)
    1889  {
    1890  iterator result = *this;
    1891  --*this;
    1892  return result;
    1893  }
    1894 
    1895  bool operator==(const iterator& rhs) const
    1896  {
    1897  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1898  return m_pItem == rhs.m_pItem;
    1899  }
    1900  bool operator!=(const iterator& rhs) const
    1901  {
    1902  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1903  return m_pItem != rhs.m_pItem;
    1904  }
    1905 
    1906  private:
    1907  VmaRawList<T>* m_pList;
    1908  VmaListItem<T>* m_pItem;
    1909 
    1910  iterator(VmaRawList<T>* pList, VmaListItem<T>* pItem) :
    1911  m_pList(pList),
    1912  m_pItem(pItem)
    1913  {
    1914  }
    1915 
    1916  friend class VmaList<T, AllocatorT>;
    1917  friend class VmaList<T, AllocatorT>:: const_iterator;
    1918  };
    1919 
    1920  class const_iterator
    1921  {
    1922  public:
    1923  const_iterator() :
    1924  m_pList(VMA_NULL),
    1925  m_pItem(VMA_NULL)
    1926  {
    1927  }
    1928 
    1929  const_iterator(const iterator& src) :
    1930  m_pList(src.m_pList),
    1931  m_pItem(src.m_pItem)
    1932  {
    1933  }
    1934 
    1935  const T& operator*() const
    1936  {
    1937  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1938  return m_pItem->Value;
    1939  }
    1940  const T* operator->() const
    1941  {
    1942  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1943  return &m_pItem->Value;
    1944  }
    1945 
    1946  const_iterator& operator++()
    1947  {
    1948  VMA_HEAVY_ASSERT(m_pItem != VMA_NULL);
    1949  m_pItem = m_pItem->pNext;
    1950  return *this;
    1951  }
    1952  const_iterator& operator--()
    1953  {
    1954  if(m_pItem != VMA_NULL)
    1955  m_pItem = m_pItem->pPrev;
    1956  else
    1957  {
    1958  VMA_HEAVY_ASSERT(!m_pList->IsEmpty());
    1959  m_pItem = m_pList->Back();
    1960  }
    1961  return *this;
    1962  }
    1963 
    1964  const_iterator operator++(int)
    1965  {
    1966  const_iterator result = *this;
    1967  ++*this;
    1968  return result;
    1969  }
    1970  const_iterator operator--(int)
    1971  {
    1972  const_iterator result = *this;
    1973  --*this;
    1974  return result;
    1975  }
    1976 
    1977  bool operator==(const const_iterator& rhs) const
    1978  {
    1979  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1980  return m_pItem == rhs.m_pItem;
    1981  }
    1982  bool operator!=(const const_iterator& rhs) const
    1983  {
    1984  VMA_HEAVY_ASSERT(m_pList == rhs.m_pList);
    1985  return m_pItem != rhs.m_pItem;
    1986  }
    1987 
    1988  private:
    1989  const_iterator(const VmaRawList<T>* pList, const VmaListItem<T>* pItem) :
    1990  m_pList(pList),
    1991  m_pItem(pItem)
    1992  {
    1993  }
    1994 
    1995  const VmaRawList<T>* m_pList;
    1996  const VmaListItem<T>* m_pItem;
    1997 
    1998  friend class VmaList<T, AllocatorT>;
    1999  };
    2000 
    2001  VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) { }
    2002 
    2003  bool empty() const { return m_RawList.IsEmpty(); }
    2004  size_t size() const { return m_RawList.GetCount(); }
    2005 
    2006  iterator begin() { return iterator(&m_RawList, m_RawList.Front()); }
    2007  iterator end() { return iterator(&m_RawList, VMA_NULL); }
    2008 
    2009  const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); }
    2010  const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); }
    2011 
    2012  void clear() { m_RawList.Clear(); }
    2013  void push_back(const T& value) { m_RawList.PushBack(value); }
    2014  void erase(iterator it) { m_RawList.Remove(it.m_pItem); }
    2015  iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); }
    2016 
    2017 private:
    2018  VmaRawList<T> m_RawList;
    2019 };
    2020 
    2021 #endif // #if VMA_USE_STL_LIST
    2022 
    2024 // class VmaMap
    2025 
    2026 #if VMA_USE_STL_UNORDERED_MAP
    2027 
    2028 #define VmaPair std::pair
    2029 
    2030 #define VMA_MAP_TYPE(KeyT, ValueT) \
    2031  std::unordered_map< KeyT, ValueT, std::hash<KeyT>, std::equal_to<KeyT>, VmaStlAllocator< std::pair<KeyT, ValueT> > >
    2032 
    2033 #else // #if VMA_USE_STL_UNORDERED_MAP
    2034 
    2035 template<typename T1, typename T2>
    2036 struct VmaPair
    2037 {
    2038  T1 first;
    2039  T2 second;
    2040 
    2041  VmaPair() : first(), second() { }
    2042  VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) { }
    2043 };
    2044 
    2045 /* Class compatible with subset of interface of std::unordered_map.
    2046 KeyT, ValueT must be POD because they will be stored in VmaVector.
    2047 */
    2048 template<typename KeyT, typename ValueT>
    2049 class VmaMap
    2050 {
    2051 public:
    2052  typedef VmaPair<KeyT, ValueT> PairType;
    2053  typedef PairType* iterator;
    2054 
    2055  VmaMap(const VmaStlAllocator<PairType>& allocator) : m_Vector(allocator) { }
    2056 
    2057  iterator begin() { return m_Vector.begin(); }
    2058  iterator end() { return m_Vector.end(); }
    2059 
    2060  void insert(const PairType& pair);
    2061  iterator find(const KeyT& key);
    2062  void erase(iterator it);
    2063 
    2064 private:
    2065  VmaVector< PairType, VmaStlAllocator<PairType> > m_Vector;
    2066 };
    2067 
    2068 #define VMA_MAP_TYPE(KeyT, ValueT) VmaMap<KeyT, ValueT>
    2069 
    2070 template<typename FirstT, typename SecondT>
    2071 struct VmaPairFirstLess
    2072 {
    2073  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const VmaPair<FirstT, SecondT>& rhs) const
    2074  {
    2075  return lhs.first < rhs.first;
    2076  }
    2077  bool operator()(const VmaPair<FirstT, SecondT>& lhs, const FirstT& rhsFirst) const
    2078  {
    2079  return lhs.first < rhsFirst;
    2080  }
    2081 };
    2082 
    2083 template<typename KeyT, typename ValueT>
    2084 void VmaMap<KeyT, ValueT>::insert(const PairType& pair)
    2085 {
    2086  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
    2087  m_Vector.data(),
    2088  m_Vector.data() + m_Vector.size(),
    2089  pair,
    2090  VmaPairFirstLess<KeyT, ValueT>()) - m_Vector.data();
    2091  VectorInsert(m_Vector, indexToInsert, pair);
    2092 }
    2093 
    2094 template<typename KeyT, typename ValueT>
    2095 VmaPair<KeyT, ValueT>* VmaMap<KeyT, ValueT>::find(const KeyT& key)
    2096 {
    2097  PairType* it = VmaBinaryFindFirstNotLess(
    2098  m_Vector.data(),
    2099  m_Vector.data() + m_Vector.size(),
    2100  key,
    2101  VmaPairFirstLess<KeyT, ValueT>());
    2102  if((it != m_Vector.end()) && (it->first == key))
    2103  return it;
    2104  else
    2105  return m_Vector.end();
    2106 }
    2107 
    2108 template<typename KeyT, typename ValueT>
    2109 void VmaMap<KeyT, ValueT>::erase(iterator it)
    2110 {
    2111  VectorRemove(m_Vector, it - m_Vector.begin());
    2112 }
    2113 
    2114 #endif // #if VMA_USE_STL_UNORDERED_MAP
    2115 
    2117 
    2118 class VmaBlock;
    2119 
    2120 enum VMA_BLOCK_VECTOR_TYPE
    2121 {
    2122  VMA_BLOCK_VECTOR_TYPE_UNMAPPED,
    2123  VMA_BLOCK_VECTOR_TYPE_MAPPED,
    2124  VMA_BLOCK_VECTOR_TYPE_COUNT
    2125 };
    2126 
    2127 static VMA_BLOCK_VECTOR_TYPE VmaMemoryRequirementFlagsToBlockVectorType(VmaMemoryRequirementFlags flags)
    2128 {
    2129  return (flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0 ?
    2130  VMA_BLOCK_VECTOR_TYPE_MAPPED :
    2131  VMA_BLOCK_VECTOR_TYPE_UNMAPPED;
    2132 }
    2133 
    2134 struct VmaAllocation_T
    2135 {
    2136 public:
    2137  enum ALLOCATION_TYPE
    2138  {
    2139  ALLOCATION_TYPE_NONE,
    2140  ALLOCATION_TYPE_BLOCK,
    2141  ALLOCATION_TYPE_OWN,
    2142  };
    2143 
    2144  VmaAllocation_T()
    2145  {
    2146  memset(this, 0, sizeof(VmaAllocation_T));
    2147  }
    2148 
    2149  void InitBlockAllocation(
    2150  VmaBlock* block,
    2151  VkDeviceSize offset,
    2152  VkDeviceSize alignment,
    2153  VkDeviceSize size,
    2154  VmaSuballocationType suballocationType,
    2155  void* pUserData)
    2156  {
    2157  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
    2158  VMA_ASSERT(block != VMA_NULL);
    2159  m_Type = ALLOCATION_TYPE_BLOCK;
    2160  m_Alignment = alignment;
    2161  m_Size = size;
    2162  m_pUserData = pUserData;
    2163  m_SuballocationType = suballocationType;
    2164  m_BlockAllocation.m_Block = block;
    2165  m_BlockAllocation.m_Offset = offset;
    2166  }
    2167 
    2168  void ChangeBlockAllocation(
    2169  VmaBlock* block,
    2170  VkDeviceSize offset)
    2171  {
    2172  VMA_ASSERT(block != VMA_NULL);
    2173  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
    2174  m_BlockAllocation.m_Block = block;
    2175  m_BlockAllocation.m_Offset = offset;
    2176  }
    2177 
    2178  void InitOwnAllocation(
    2179  uint32_t memoryTypeIndex,
    2180  VkDeviceMemory hMemory,
    2181  VmaSuballocationType suballocationType,
    2182  bool persistentMap,
    2183  void* pMappedData,
    2184  VkDeviceSize size,
    2185  void* pUserData)
    2186  {
    2187  VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE);
    2188  VMA_ASSERT(hMemory != VK_NULL_HANDLE);
    2189  m_Type = ALLOCATION_TYPE_OWN;
    2190  m_Alignment = 0;
    2191  m_Size = size;
    2192  m_pUserData = pUserData;
    2193  m_SuballocationType = suballocationType;
    2194  m_OwnAllocation.m_MemoryTypeIndex = memoryTypeIndex;
    2195  m_OwnAllocation.m_hMemory = hMemory;
    2196  m_OwnAllocation.m_PersistentMap = persistentMap;
    2197  m_OwnAllocation.m_pMappedData = pMappedData;
    2198  }
    2199 
    2200  ALLOCATION_TYPE GetType() const { return m_Type; }
    2201  VkDeviceSize GetAlignment() const { return m_Alignment; }
    2202  VkDeviceSize GetSize() const { return m_Size; }
    2203  void* GetUserData() const { return m_pUserData; }
    2204  void SetUserData(void* pUserData) { m_pUserData = pUserData; }
    2205  VmaSuballocationType GetSuballocationType() const { return m_SuballocationType; }
    2206 
    2207  VmaBlock* GetBlock() const
    2208  {
    2209  VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK);
    2210  return m_BlockAllocation.m_Block;
    2211  }
    2212  VkDeviceSize GetOffset() const
    2213  {
    2214  return (m_Type == ALLOCATION_TYPE_BLOCK) ? m_BlockAllocation.m_Offset : 0;
    2215  }
    2216  VkDeviceMemory GetMemory() const;
    2217  uint32_t GetMemoryTypeIndex() const;
    2218  VMA_BLOCK_VECTOR_TYPE GetBlockVectorType() const;
    2219  void* GetMappedData() const;
    2220 
    2221  VkResult OwnAllocMapPersistentlyMappedMemory(VkDevice hDevice)
    2222  {
    2223  VMA_ASSERT(m_Type == ALLOCATION_TYPE_OWN);
    2224  if(m_OwnAllocation.m_PersistentMap)
    2225  {
    2226  return vkMapMemory(hDevice, m_OwnAllocation.m_hMemory, 0, VK_WHOLE_SIZE, 0, &m_OwnAllocation.m_pMappedData);
    2227  }
    2228  return VK_SUCCESS;
    2229  }
    2230  void OwnAllocUnmapPersistentlyMappedMemory(VkDevice hDevice)
    2231  {
    2232  VMA_ASSERT(m_Type == ALLOCATION_TYPE_OWN);
    2233  if(m_OwnAllocation.m_pMappedData)
    2234  {
    2235  VMA_ASSERT(m_OwnAllocation.m_PersistentMap);
    2236  vkUnmapMemory(hDevice, m_OwnAllocation.m_hMemory);
    2237  m_OwnAllocation.m_pMappedData = VMA_NULL;
    2238  }
    2239  }
    2240 
    2241 private:
    2242  VkDeviceSize m_Alignment;
    2243  VkDeviceSize m_Size;
    2244  void* m_pUserData;
    2245  ALLOCATION_TYPE m_Type;
    2246  VmaSuballocationType m_SuballocationType;
    2247 
    2248  union
    2249  {
    2250  // Allocation out of VmaBlock.
    2251  struct BlockAllocation
    2252  {
    2253  VmaBlock* m_Block;
    2254  VkDeviceSize m_Offset;
    2255  } m_BlockAllocation;
    2256 
    2257  // Allocation for an object that has its own private VkDeviceMemory.
    2258  struct OwnAllocation
    2259  {
    2260  uint32_t m_MemoryTypeIndex;
    2261  VkDeviceMemory m_hMemory;
    2262  bool m_PersistentMap;
    2263  void* m_pMappedData;
    2264  } m_OwnAllocation;
    2265  };
    2266 };
    2267 
    2268 /*
    2269 Represents a region of VmaBlock that is either assigned and returned as
    2270 allocated memory block or free.
    2271 */
    2272 struct VmaSuballocation
    2273 {
    2274  VkDeviceSize offset;
    2275  VkDeviceSize size;
    2276  VmaSuballocationType type;
    2277 };
    2278 
    2279 typedef VmaList< VmaSuballocation, VmaStlAllocator<VmaSuballocation> > VmaSuballocationList;
    2280 
    2281 // Parameters of an allocation.
    2282 struct VmaAllocationRequest
    2283 {
    2284  VmaSuballocationList::iterator freeSuballocationItem;
    2285  VkDeviceSize offset;
    2286 };
    2287 
    2288 /* Single block of memory - VkDeviceMemory with all the data about its regions
    2289 assigned or free. */
    2290 class VmaBlock
    2291 {
    2292 public:
    2293  uint32_t m_MemoryTypeIndex;
    2294  VMA_BLOCK_VECTOR_TYPE m_BlockVectorType;
    2295  VkDeviceMemory m_hMemory;
    2296  VkDeviceSize m_Size;
    2297  bool m_PersistentMap;
    2298  void* m_pMappedData;
    2299  uint32_t m_FreeCount;
    2300  VkDeviceSize m_SumFreeSize;
    2301  VmaSuballocationList m_Suballocations;
    2302  // Suballocations that are free and have size greater than certain threshold.
    2303  // Sorted by size, ascending.
    2304  VmaVector< VmaSuballocationList::iterator, VmaStlAllocator< VmaSuballocationList::iterator > > m_FreeSuballocationsBySize;
    2305 
    2306  VmaBlock(VmaAllocator hAllocator);
    2307 
    2308  ~VmaBlock()
    2309  {
    2310  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
    2311  }
    2312 
    2313  // Always call after construction.
    2314  void Init(
    2315  uint32_t newMemoryTypeIndex,
    2316  VMA_BLOCK_VECTOR_TYPE newBlockVectorType,
    2317  VkDeviceMemory newMemory,
    2318  VkDeviceSize newSize,
    2319  bool persistentMap,
    2320  void* pMappedData);
    2321  // Always call before destruction.
    2322  void Destroy(VmaAllocator allocator);
    2323 
    2324  // Validates all data structures inside this object. If not valid, returns false.
    2325  bool Validate() const;
    2326 
    2327  // Tries to find a place for suballocation with given parameters inside this allocation.
    2328  // If succeeded, fills pAllocationRequest and returns true.
    2329  // If failed, returns false.
    2330  bool CreateAllocationRequest(
    2331  VkDeviceSize bufferImageGranularity,
    2332  VkDeviceSize allocSize,
    2333  VkDeviceSize allocAlignment,
    2334  VmaSuballocationType allocType,
    2335  VmaAllocationRequest* pAllocationRequest);
    2336 
    2337  // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem.
    2338  // If yes, fills pOffset and returns true. If no, returns false.
    2339  bool CheckAllocation(
    2340  VkDeviceSize bufferImageGranularity,
    2341  VkDeviceSize allocSize,
    2342  VkDeviceSize allocAlignment,
    2343  VmaSuballocationType allocType,
    2344  VmaSuballocationList::const_iterator freeSuballocItem,
    2345  VkDeviceSize* pOffset) const;
    2346 
    2347  // Returns true if this allocation is empty - contains only single free suballocation.
    2348  bool IsEmpty() const;
    2349 
    2350  // Makes actual allocation based on request. Request must already be checked
    2351  // and valid.
    2352  void Alloc(
    2353  const VmaAllocationRequest& request,
    2354  VmaSuballocationType type,
    2355  VkDeviceSize allocSize);
    2356 
    2357  // Frees suballocation assigned to given memory region.
    2358  void Free(const VmaAllocation allocation);
    2359 
    2360 #if VMA_STATS_STRING_ENABLED
    2361  void PrintDetailedMap(class VmaStringBuilder& sb) const;
    2362 #endif
    2363 
    2364 private:
    2365  // Given free suballocation, it merges it with following one, which must also be free.
    2366  void MergeFreeWithNext(VmaSuballocationList::iterator item);
    2367  // Releases given suballocation, making it free. Merges it with adjacent free
    2368  // suballocations if applicable.
    2369  void FreeSuballocation(VmaSuballocationList::iterator suballocItem);
    2370  // Given free suballocation, it inserts it into sorted list of
    2371  // m_FreeSuballocationsBySize if it's suitable.
    2372  void RegisterFreeSuballocation(VmaSuballocationList::iterator item);
    2373  // Given free suballocation, it removes it from sorted list of
    2374  // m_FreeSuballocationsBySize if it's suitable.
    2375  void UnregisterFreeSuballocation(VmaSuballocationList::iterator item);
    2376 };
    2377 
    2378 struct VmaPointerLess
    2379 {
    2380  bool operator()(const void* lhs, const void* rhs) const
    2381  {
    2382  return lhs < rhs;
    2383  }
    2384 };
    2385 
    2386 /* Sequence of VmaBlock. Represents memory blocks allocated for a specific
    2387 Vulkan memory type. */
    2388 struct VmaBlockVector
    2389 {
    2390  // Incrementally sorted by sumFreeSize, ascending.
    2391  VmaVector< VmaBlock*, VmaStlAllocator<VmaBlock*> > m_Blocks;
    2392 
    2393  VmaBlockVector(VmaAllocator hAllocator);
    2394  ~VmaBlockVector();
    2395 
    2396  bool IsEmpty() const { return m_Blocks.empty(); }
    2397 
    2398  // Finds and removes given block from vector.
    2399  void Remove(VmaBlock* pBlock);
    2400 
    2401  // Performs single step in sorting m_Blocks. They may not be fully sorted
    2402  // after this call.
    2403  void IncrementallySortBlocks();
    2404 
    2405  // Adds statistics of this BlockVector to pStats.
    2406  void AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const;
    2407 
    2408 #if VMA_STATS_STRING_ENABLED
    2409  void PrintDetailedMap(class VmaStringBuilder& sb) const;
    2410 #endif
    2411 
    2412  void UnmapPersistentlyMappedMemory();
    2413  VkResult MapPersistentlyMappedMemory();
    2414 
    2415 private:
    2416  VmaAllocator m_hAllocator;
    2417 };
    2418 
    2419 // Main allocator object.
    2420 struct VmaAllocator_T
    2421 {
    2422  bool m_UseMutex;
    2423  VkDevice m_hDevice;
    2424  bool m_AllocationCallbacksSpecified;
    2425  VkAllocationCallbacks m_AllocationCallbacks;
    2426  VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks;
    2427  VkDeviceSize m_PreferredLargeHeapBlockSize;
    2428  VkDeviceSize m_PreferredSmallHeapBlockSize;
    2429  // Non-zero when we are inside UnmapPersistentlyMappedMemory...MapPersistentlyMappedMemory.
    2430  // Counter to allow nested calls to these functions.
    2431  uint32_t m_UnmapPersistentlyMappedMemoryCounter;
    2432 
    2433  VkPhysicalDeviceProperties m_PhysicalDeviceProperties;
    2434  VkPhysicalDeviceMemoryProperties m_MemProps;
    2435 
    2436  VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT];
    2437  /* There can be at most one allocation that is completely empty - a
    2438  hysteresis to avoid pessimistic case of alternating creation and destruction
    2439  of a VkDeviceMemory. */
    2440  bool m_HasEmptyBlock[VK_MAX_MEMORY_TYPES];
    2441  VMA_MUTEX m_BlocksMutex[VK_MAX_MEMORY_TYPES];
    2442 
    2443  // Each vector is sorted by memory (handle value).
    2444  typedef VmaVector< VmaAllocation, VmaStlAllocator<VmaAllocation> > AllocationVectorType;
    2445  AllocationVectorType* m_pOwnAllocations[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT];
    2446  VMA_MUTEX m_OwnAllocationsMutex[VK_MAX_MEMORY_TYPES];
    2447 
    2448  VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo);
    2449  ~VmaAllocator_T();
    2450 
    2451  const VkAllocationCallbacks* GetAllocationCallbacks() const
    2452  {
    2453  return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : 0;
    2454  }
    2455 
    2456  VkDeviceSize GetPreferredBlockSize(uint32_t memTypeIndex) const;
    2457 
    2458  VkDeviceSize GetBufferImageGranularity() const
    2459  {
    2460  return VMA_MAX(
    2461  static_cast<VkDeviceSize>(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY),
    2462  m_PhysicalDeviceProperties.limits.bufferImageGranularity);
    2463  }
    2464 
    2465  uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; }
    2466  uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; }
    2467 
    2468  // Main allocation function.
    2469  VkResult AllocateMemory(
    2470  const VkMemoryRequirements& vkMemReq,
    2471  const VmaMemoryRequirements& vmaMemReq,
    2472  VmaSuballocationType suballocType,
    2473  VmaAllocation* pAllocation);
    2474 
    2475  // Main deallocation function.
    2476  void FreeMemory(const VmaAllocation allocation);
    2477 
    2478  void CalculateStats(VmaStats* pStats);
    2479 
    2480 #if VMA_STATS_STRING_ENABLED
    2481  void PrintDetailedMap(class VmaStringBuilder& sb);
    2482 #endif
    2483 
    2484  void UnmapPersistentlyMappedMemory();
    2485  VkResult MapPersistentlyMappedMemory();
    2486 
    2487  VkResult Defragment(
    2488  VmaAllocation* pAllocations,
    2489  size_t allocationCount,
    2490  VkBool32* pAllocationsChanged,
    2491  const VmaDefragmentationInfo* pDefragmentationInfo,
    2492  VmaDefragmentationStats* pDefragmentationStats);
    2493 
    2494  static void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo);
    2495 
    2496 private:
    2497  VkPhysicalDevice m_PhysicalDevice;
    2498 
    2499  VkResult AllocateMemoryOfType(
    2500  const VkMemoryRequirements& vkMemReq,
    2501  const VmaMemoryRequirements& vmaMemReq,
    2502  uint32_t memTypeIndex,
    2503  VmaSuballocationType suballocType,
    2504  VmaAllocation* pAllocation);
    2505 
    2506  // Allocates and registers new VkDeviceMemory specifically for single allocation.
    2507  VkResult AllocateOwnMemory(
    2508  VkDeviceSize size,
    2509  VmaSuballocationType suballocType,
    2510  uint32_t memTypeIndex,
    2511  bool map,
    2512  void* pUserData,
    2513  VmaAllocation* pAllocation);
    2514 
    2515  // Tries to free pMemory as Own Memory. Returns true if found and freed.
    2516  void FreeOwnMemory(VmaAllocation allocation);
    2517 };
    2518 
    2520 // Memory allocation #2 after VmaAllocator_T definition
    2521 
    2522 static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment)
    2523 {
    2524  return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment);
    2525 }
    2526 
    2527 static void VmaFree(VmaAllocator hAllocator, void* ptr)
    2528 {
    2529  VmaFree(&hAllocator->m_AllocationCallbacks, ptr);
    2530 }
    2531 
    2532 template<typename T>
    2533 static T* VmaAllocate(VmaAllocator hAllocator)
    2534 {
    2535  return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
    2536 }
    2537 
    2538 template<typename T>
    2539 static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
    2540 {
    2541  return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
    2542 }
    2543 
    2544 template<typename T>
    2545 static void vma_delete(VmaAllocator hAllocator, T* ptr)
    2546 {
    2547  if(ptr != VMA_NULL)
    2548  {
    2549  ptr->~T();
    2550  VmaFree(hAllocator, ptr);
    2551  }
    2552 }
    2553 
    2554 template<typename T>
    2555 static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
    2556 {
    2557  if(ptr != VMA_NULL)
    2558  {
    2559  for(size_t i = count; i--; )
    2560  ptr[i].~T();
    2561  VmaFree(hAllocator, ptr);
    2562  }
    2563 }
    2564 
    2566 // VmaStringBuilder
    2567 
    2568 #if VMA_STATS_STRING_ENABLED
    2569 
    2570 class VmaStringBuilder
    2571 {
    2572 public:
    2573  VmaStringBuilder(VmaAllocator alloc) : m_Data(VmaStlAllocator<char>(alloc->GetAllocationCallbacks())) { }
    2574  size_t GetLength() const { return m_Data.size(); }
    2575  const char* GetData() const { return m_Data.data(); }
    2576 
    2577  void Add(char ch) { m_Data.push_back(ch); }
    2578  void Add(const char* pStr);
    2579  void AddNewLine() { Add('\n'); }
    2580  void AddNumber(uint32_t num);
    2581  void AddNumber(uint64_t num);
    2582  void AddBool(bool b) { Add(b ? "true" : "false"); }
    2583  void AddNull() { Add("null"); }
    2584  void AddString(const char* pStr);
    2585 
    2586 private:
    2587  VmaVector< char, VmaStlAllocator<char> > m_Data;
    2588 };
    2589 
    2590 void VmaStringBuilder::Add(const char* pStr)
    2591 {
    2592  const size_t strLen = strlen(pStr);
    2593  if(strLen > 0)
    2594  {
    2595  const size_t oldCount = m_Data.size();
    2596  m_Data.resize(oldCount + strLen);
    2597  memcpy(m_Data.data() + oldCount, pStr, strLen);
    2598  }
    2599 }
    2600 
    2601 void VmaStringBuilder::AddNumber(uint32_t num)
    2602 {
    2603  char buf[11];
    2604  VmaUint32ToStr(buf, sizeof(buf), num);
    2605  Add(buf);
    2606 }
    2607 
    2608 void VmaStringBuilder::AddNumber(uint64_t num)
    2609 {
    2610  char buf[21];
    2611  VmaUint64ToStr(buf, sizeof(buf), num);
    2612  Add(buf);
    2613 }
    2614 
    2615 void VmaStringBuilder::AddString(const char* pStr)
    2616 {
    2617  Add('"');
    2618  const size_t strLen = strlen(pStr);
    2619  for(size_t i = 0; i < strLen; ++i)
    2620  {
    2621  char ch = pStr[i];
    2622  if(ch == '\'')
    2623  Add("\\\\");
    2624  else if(ch == '"')
    2625  Add("\\\"");
    2626  else if(ch >= 32)
    2627  Add(ch);
    2628  else switch(ch)
    2629  {
    2630  case '\n':
    2631  Add("\\n");
    2632  break;
    2633  case '\r':
    2634  Add("\\r");
    2635  break;
    2636  case '\t':
    2637  Add("\\t");
    2638  break;
    2639  default:
    2640  VMA_ASSERT(0 && "Character not currently supported.");
    2641  break;
    2642  }
    2643  }
    2644  Add('"');
    2645 }
    2646 
    2648 
    2649 VkDeviceMemory VmaAllocation_T::GetMemory() const
    2650 {
    2651  return (m_Type == ALLOCATION_TYPE_BLOCK) ?
    2652  m_BlockAllocation.m_Block->m_hMemory : m_OwnAllocation.m_hMemory;
    2653 }
    2654 
    2655 uint32_t VmaAllocation_T::GetMemoryTypeIndex() const
    2656 {
    2657  return (m_Type == ALLOCATION_TYPE_BLOCK) ?
    2658  m_BlockAllocation.m_Block->m_MemoryTypeIndex : m_OwnAllocation.m_MemoryTypeIndex;
    2659 }
    2660 
    2661 VMA_BLOCK_VECTOR_TYPE VmaAllocation_T::GetBlockVectorType() const
    2662 {
    2663  return (m_Type == ALLOCATION_TYPE_BLOCK) ?
    2664  m_BlockAllocation.m_Block->m_BlockVectorType :
    2665  (m_OwnAllocation.m_PersistentMap ? VMA_BLOCK_VECTOR_TYPE_MAPPED : VMA_BLOCK_VECTOR_TYPE_UNMAPPED);
    2666 }
    2667 
    2668 void* VmaAllocation_T::GetMappedData() const
    2669 {
    2670  switch(m_Type)
    2671  {
    2672  case ALLOCATION_TYPE_BLOCK:
    2673  if(m_BlockAllocation.m_Block->m_pMappedData != VMA_NULL)
    2674  {
    2675  return (char*)m_BlockAllocation.m_Block->m_pMappedData + m_BlockAllocation.m_Offset;
    2676  }
    2677  else
    2678  {
    2679  return VMA_NULL;
    2680  }
    2681  break;
    2682  case ALLOCATION_TYPE_OWN:
    2683  return m_OwnAllocation.m_pMappedData;
    2684  default:
    2685  VMA_ASSERT(0);
    2686  return VMA_NULL;
    2687  }
    2688 }
    2689 
    2690 // Correspond to values of enum VmaSuballocationType.
    2691 static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = {
    2692  "FREE",
    2693  "UNKNOWN",
    2694  "BUFFER",
    2695  "IMAGE_UNKNOWN",
    2696  "IMAGE_LINEAR",
    2697  "IMAGE_OPTIMAL",
    2698 };
    2699 
    2700 static void VmaPrintStatInfo(VmaStringBuilder& sb, const VmaStatInfo& stat)
    2701 {
    2702  sb.Add("{ \"Allocations\": ");
    2703  sb.AddNumber(stat.AllocationCount);
    2704  sb.Add(", \"Suballocations\": ");
    2705  sb.AddNumber(stat.SuballocationCount);
    2706  sb.Add(", \"UnusedRanges\": ");
    2707  sb.AddNumber(stat.UnusedRangeCount);
    2708  sb.Add(", \"UsedBytes\": ");
    2709  sb.AddNumber(stat.UsedBytes);
    2710  sb.Add(", \"UnusedBytes\": ");
    2711  sb.AddNumber(stat.UnusedBytes);
    2712  sb.Add(", \"SuballocationSize\": { \"Min\": ");
    2713  sb.AddNumber(stat.SuballocationSizeMin);
    2714  sb.Add(", \"Avg\": ");
    2715  sb.AddNumber(stat.SuballocationSizeAvg);
    2716  sb.Add(", \"Max\": ");
    2717  sb.AddNumber(stat.SuballocationSizeMax);
    2718  sb.Add(" }, \"UnusedRangeSize\": { \"Min\": ");
    2719  sb.AddNumber(stat.UnusedRangeSizeMin);
    2720  sb.Add(", \"Avg\": ");
    2721  sb.AddNumber(stat.UnusedRangeSizeAvg);
    2722  sb.Add(", \"Max\": ");
    2723  sb.AddNumber(stat.UnusedRangeSizeMax);
    2724  sb.Add(" } }");
    2725 }
    2726 
    2727 #endif // #if VMA_STATS_STRING_ENABLED
    2728 
    2729 struct VmaSuballocationItemSizeLess
    2730 {
    2731  bool operator()(
    2732  const VmaSuballocationList::iterator lhs,
    2733  const VmaSuballocationList::iterator rhs) const
    2734  {
    2735  return lhs->size < rhs->size;
    2736  }
    2737  bool operator()(
    2738  const VmaSuballocationList::iterator lhs,
    2739  VkDeviceSize rhsSize) const
    2740  {
    2741  return lhs->size < rhsSize;
    2742  }
    2743 };
    2744 
    2745 VmaBlock::VmaBlock(VmaAllocator hAllocator) :
    2746  m_MemoryTypeIndex(UINT32_MAX),
    2747  m_BlockVectorType(VMA_BLOCK_VECTOR_TYPE_COUNT),
    2748  m_hMemory(VK_NULL_HANDLE),
    2749  m_Size(0),
    2750  m_PersistentMap(false),
    2751  m_pMappedData(VMA_NULL),
    2752  m_FreeCount(0),
    2753  m_SumFreeSize(0),
    2754  m_Suballocations(VmaStlAllocator<VmaSuballocation>(hAllocator->GetAllocationCallbacks())),
    2755  m_FreeSuballocationsBySize(VmaStlAllocator<VmaSuballocationList::iterator>(hAllocator->GetAllocationCallbacks()))
    2756 {
    2757 }
    2758 
    2759 void VmaBlock::Init(
    2760  uint32_t newMemoryTypeIndex,
    2761  VMA_BLOCK_VECTOR_TYPE newBlockVectorType,
    2762  VkDeviceMemory newMemory,
    2763  VkDeviceSize newSize,
    2764  bool persistentMap,
    2765  void* pMappedData)
    2766 {
    2767  VMA_ASSERT(m_hMemory == VK_NULL_HANDLE);
    2768 
    2769  m_MemoryTypeIndex = newMemoryTypeIndex;
    2770  m_BlockVectorType = newBlockVectorType;
    2771  m_hMemory = newMemory;
    2772  m_Size = newSize;
    2773  m_PersistentMap = persistentMap;
    2774  m_pMappedData = pMappedData;
    2775  m_FreeCount = 1;
    2776  m_SumFreeSize = newSize;
    2777 
    2778  m_Suballocations.clear();
    2779  m_FreeSuballocationsBySize.clear();
    2780 
    2781  VmaSuballocation suballoc = {};
    2782  suballoc.offset = 0;
    2783  suballoc.size = newSize;
    2784  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    2785 
    2786  m_Suballocations.push_back(suballoc);
    2787  VmaSuballocationList::iterator suballocItem = m_Suballocations.end();
    2788  --suballocItem;
    2789  m_FreeSuballocationsBySize.push_back(suballocItem);
    2790 }
    2791 
    2792 void VmaBlock::Destroy(VmaAllocator allocator)
    2793 {
    2794  VMA_ASSERT(m_hMemory != VK_NULL_HANDLE);
    2795  if(m_pMappedData != VMA_NULL)
    2796  {
    2797  vkUnmapMemory(allocator->m_hDevice, m_hMemory);
    2798  m_pMappedData = VMA_NULL;
    2799  }
    2800 
    2801  // Callback.
    2802  if(allocator->m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
    2803  {
    2804  (*allocator->m_DeviceMemoryCallbacks.pfnFree)(allocator, m_MemoryTypeIndex, m_hMemory, m_Size);
    2805  }
    2806 
    2807  vkFreeMemory(allocator->m_hDevice, m_hMemory, allocator->GetAllocationCallbacks());
    2808  m_hMemory = VK_NULL_HANDLE;
    2809 }
    2810 
    2811 bool VmaBlock::Validate() const
    2812 {
    2813  if((m_hMemory == VK_NULL_HANDLE) ||
    2814  (m_Size == 0) ||
    2815  m_Suballocations.empty())
    2816  {
    2817  return false;
    2818  }
    2819 
    2820  // Expected offset of new suballocation as calculates from previous ones.
    2821  VkDeviceSize calculatedOffset = 0;
    2822  // Expected number of free suballocations as calculated from traversing their list.
    2823  uint32_t calculatedFreeCount = 0;
    2824  // Expected sum size of free suballocations as calculated from traversing their list.
    2825  VkDeviceSize calculatedSumFreeSize = 0;
    2826  // Expected number of free suballocations that should be registered in
    2827  // m_FreeSuballocationsBySize calculated from traversing their list.
    2828  size_t freeSuballocationsToRegister = 0;
    2829  // True if previous visisted suballocation was free.
    2830  bool prevFree = false;
    2831 
    2832  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
    2833  suballocItem != m_Suballocations.cend();
    2834  ++suballocItem)
    2835  {
    2836  const VmaSuballocation& subAlloc = *suballocItem;
    2837 
    2838  // Actual offset of this suballocation doesn't match expected one.
    2839  if(subAlloc.offset != calculatedOffset)
    2840  return false;
    2841 
    2842  const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE);
    2843  // Two adjacent free suballocations are invalid. They should be merged.
    2844  if(prevFree && currFree)
    2845  return false;
    2846  prevFree = currFree;
    2847 
    2848  if(currFree)
    2849  {
    2850  calculatedSumFreeSize += subAlloc.size;
    2851  ++calculatedFreeCount;
    2852  if(subAlloc.size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    2853  ++freeSuballocationsToRegister;
    2854  }
    2855 
    2856  calculatedOffset += subAlloc.size;
    2857  }
    2858 
    2859  // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't
    2860  // match expected one.
    2861  if(m_FreeSuballocationsBySize.size() != freeSuballocationsToRegister)
    2862  return false;
    2863 
    2864  VkDeviceSize lastSize = 0;
    2865  for(size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i)
    2866  {
    2867  VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i];
    2868 
    2869  // Only free suballocations can be registered in m_FreeSuballocationsBySize.
    2870  if(suballocItem->type != VMA_SUBALLOCATION_TYPE_FREE)
    2871  return false;
    2872  // They must be sorted by size ascending.
    2873  if(suballocItem->size < lastSize)
    2874  return false;
    2875 
    2876  lastSize = suballocItem->size;
    2877  }
    2878 
    2879  // Check if totals match calculacted values.
    2880  return
    2881  (calculatedOffset == m_Size) &&
    2882  (calculatedSumFreeSize == m_SumFreeSize) &&
    2883  (calculatedFreeCount == m_FreeCount);
    2884 }
    2885 
    2886 /*
    2887 How many suitable free suballocations to analyze before choosing best one.
    2888 - Set to 1 to use First-Fit algorithm - first suitable free suballocation will
    2889  be chosen.
    2890 - Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
    2891  suballocations will be analized and best one will be chosen.
    2892 - Any other value is also acceptable.
    2893 */
    2894 //static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
    2895 
    2896 bool VmaBlock::CreateAllocationRequest(
    2897  VkDeviceSize bufferImageGranularity,
    2898  VkDeviceSize allocSize,
    2899  VkDeviceSize allocAlignment,
    2900  VmaSuballocationType allocType,
    2901  VmaAllocationRequest* pAllocationRequest)
    2902 {
    2903  VMA_ASSERT(allocSize > 0);
    2904  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
    2905  VMA_ASSERT(pAllocationRequest != VMA_NULL);
    2906  VMA_HEAVY_ASSERT(Validate());
    2907 
    2908  // There is not enough total free space in this allocation to fullfill the request: Early return.
    2909  if(m_SumFreeSize < allocSize)
    2910  return false;
    2911 
    2912  // Old brute-force algorithm, linearly searching suballocations.
    2913  /*
    2914  uint32_t suitableSuballocationsFound = 0;
    2915  for(VmaSuballocationList::iterator suballocItem = suballocations.Front();
    2916  suballocItem != VMA_NULL &&
    2917  suitableSuballocationsFound < MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK;
    2918  suballocItem = suballocItem->Next)
    2919  {
    2920  if(suballocItem->Value.type == VMA_SUBALLOCATION_TYPE_FREE)
    2921  {
    2922  VkDeviceSize offset = 0, cost = 0;
    2923  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset, &cost))
    2924  {
    2925  ++suitableSuballocationsFound;
    2926  if(cost < costLimit)
    2927  {
    2928  pAllocationRequest->freeSuballocationItem = suballocItem;
    2929  pAllocationRequest->offset = offset;
    2930  pAllocationRequest->cost = cost;
    2931  if(cost == 0)
    2932  return true;
    2933  costLimit = cost;
    2934  betterSuballocationFound = true;
    2935  }
    2936  }
    2937  }
    2938  }
    2939  */
    2940 
    2941  // New algorithm, efficiently searching freeSuballocationsBySize.
    2942  const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
    2943  if(freeSuballocCount > 0)
    2944  {
    2945  if(VMA_BEST_FIT)
    2946  {
    2947  // Find first free suballocation with size not less than allocSize.
    2948  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    2949  m_FreeSuballocationsBySize.data(),
    2950  m_FreeSuballocationsBySize.data() + freeSuballocCount,
    2951  allocSize,
    2952  VmaSuballocationItemSizeLess());
    2953  size_t index = it - m_FreeSuballocationsBySize.data();
    2954  for(; index < freeSuballocCount; ++index)
    2955  {
    2956  VkDeviceSize offset = 0;
    2957  const VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[index];
    2958  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset))
    2959  {
    2960  pAllocationRequest->freeSuballocationItem = suballocItem;
    2961  pAllocationRequest->offset = offset;
    2962  return true;
    2963  }
    2964  }
    2965  }
    2966  else
    2967  {
    2968  // Search staring from biggest suballocations.
    2969  for(size_t index = freeSuballocCount; index--; )
    2970  {
    2971  VkDeviceSize offset = 0;
    2972  const VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[index];
    2973  if(CheckAllocation(bufferImageGranularity, allocSize, allocAlignment, allocType, suballocItem, &offset))
    2974  {
    2975  pAllocationRequest->freeSuballocationItem = suballocItem;
    2976  pAllocationRequest->offset = offset;
    2977  return true;
    2978  }
    2979  }
    2980  }
    2981  }
    2982 
    2983  return false;
    2984 }
    2985 
    2986 bool VmaBlock::CheckAllocation(
    2987  VkDeviceSize bufferImageGranularity,
    2988  VkDeviceSize allocSize,
    2989  VkDeviceSize allocAlignment,
    2990  VmaSuballocationType allocType,
    2991  VmaSuballocationList::const_iterator freeSuballocItem,
    2992  VkDeviceSize* pOffset) const
    2993 {
    2994  VMA_ASSERT(allocSize > 0);
    2995  VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE);
    2996  VMA_ASSERT(freeSuballocItem != m_Suballocations.cend());
    2997  VMA_ASSERT(pOffset != VMA_NULL);
    2998 
    2999  const VmaSuballocation& suballoc = *freeSuballocItem;
    3000  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
    3001 
    3002  // Size of this suballocation is too small for this request: Early return.
    3003  if(suballoc.size < allocSize)
    3004  return false;
    3005 
    3006  // Start from offset equal to beginning of this suballocation.
    3007  *pOffset = suballoc.offset;
    3008 
    3009  // Apply VMA_DEBUG_MARGIN at the beginning.
    3010  if((VMA_DEBUG_MARGIN > 0) && freeSuballocItem != m_Suballocations.cbegin())
    3011  *pOffset += VMA_DEBUG_MARGIN;
    3012 
    3013  // Apply alignment.
    3014  const VkDeviceSize alignment = VMA_MAX(allocAlignment, static_cast<VkDeviceSize>(VMA_DEBUG_ALIGNMENT));
    3015  *pOffset = VmaAlignUp(*pOffset, alignment);
    3016 
    3017  // Check previous suballocations for BufferImageGranularity conflicts.
    3018  // Make bigger alignment if necessary.
    3019  if(bufferImageGranularity > 1)
    3020  {
    3021  bool bufferImageGranularityConflict = false;
    3022  VmaSuballocationList::const_iterator prevSuballocItem = freeSuballocItem;
    3023  while(prevSuballocItem != m_Suballocations.cbegin())
    3024  {
    3025  --prevSuballocItem;
    3026  const VmaSuballocation& prevSuballoc = *prevSuballocItem;
    3027  if(VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, *pOffset, bufferImageGranularity))
    3028  {
    3029  if(VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType))
    3030  {
    3031  bufferImageGranularityConflict = true;
    3032  break;
    3033  }
    3034  }
    3035  else
    3036  // Already on previous page.
    3037  break;
    3038  }
    3039  if(bufferImageGranularityConflict)
    3040  *pOffset = VmaAlignUp(*pOffset, bufferImageGranularity);
    3041  }
    3042 
    3043  // Calculate padding at the beginning based on current offset.
    3044  const VkDeviceSize paddingBegin = *pOffset - suballoc.offset;
    3045 
    3046  // Calculate required margin at the end if this is not last suballocation.
    3047  VmaSuballocationList::const_iterator next = freeSuballocItem;
    3048  ++next;
    3049  const VkDeviceSize requiredEndMargin =
    3050  (next != m_Suballocations.cend()) ? VMA_DEBUG_MARGIN : 0;
    3051 
    3052  // Fail if requested size plus margin before and after is bigger than size of this suballocation.
    3053  if(paddingBegin + allocSize + requiredEndMargin > suballoc.size)
    3054  return false;
    3055 
    3056  // Check next suballocations for BufferImageGranularity conflicts.
    3057  // If conflict exists, allocation cannot be made here.
    3058  if(bufferImageGranularity > 1)
    3059  {
    3060  VmaSuballocationList::const_iterator nextSuballocItem = freeSuballocItem;
    3061  ++nextSuballocItem;
    3062  while(nextSuballocItem != m_Suballocations.cend())
    3063  {
    3064  const VmaSuballocation& nextSuballoc = *nextSuballocItem;
    3065  if(VmaBlocksOnSamePage(*pOffset, allocSize, nextSuballoc.offset, bufferImageGranularity))
    3066  {
    3067  if(VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type))
    3068  return false;
    3069  }
    3070  else
    3071  // Already on next page.
    3072  break;
    3073  ++nextSuballocItem;
    3074  }
    3075  }
    3076 
    3077  // All tests passed: Success. pOffset is already filled.
    3078  return true;
    3079 }
    3080 
    3081 bool VmaBlock::IsEmpty() const
    3082 {
    3083  return (m_Suballocations.size() == 1) && (m_FreeCount == 1);
    3084 }
    3085 
    3086 void VmaBlock::Alloc(
    3087  const VmaAllocationRequest& request,
    3088  VmaSuballocationType type,
    3089  VkDeviceSize allocSize)
    3090 {
    3091  VMA_ASSERT(request.freeSuballocationItem != m_Suballocations.end());
    3092  VmaSuballocation& suballoc = *request.freeSuballocationItem;
    3093  // Given suballocation is a free block.
    3094  VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE);
    3095  // Given offset is inside this suballocation.
    3096  VMA_ASSERT(request.offset >= suballoc.offset);
    3097  const VkDeviceSize paddingBegin = request.offset - suballoc.offset;
    3098  VMA_ASSERT(suballoc.size >= paddingBegin + allocSize);
    3099  const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - allocSize;
    3100 
    3101  // Unregister this free suballocation from m_FreeSuballocationsBySize and update
    3102  // it to become used.
    3103  UnregisterFreeSuballocation(request.freeSuballocationItem);
    3104 
    3105  suballoc.offset = request.offset;
    3106  suballoc.size = allocSize;
    3107  suballoc.type = type;
    3108 
    3109  // If there are any free bytes remaining at the end, insert new free suballocation after current one.
    3110  if(paddingEnd)
    3111  {
    3112  VmaSuballocation paddingSuballoc = {};
    3113  paddingSuballoc.offset = request.offset + allocSize;
    3114  paddingSuballoc.size = paddingEnd;
    3115  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    3116  VmaSuballocationList::iterator next = request.freeSuballocationItem;
    3117  ++next;
    3118  const VmaSuballocationList::iterator paddingEndItem =
    3119  m_Suballocations.insert(next, paddingSuballoc);
    3120  RegisterFreeSuballocation(paddingEndItem);
    3121  }
    3122 
    3123  // If there are any free bytes remaining at the beginning, insert new free suballocation before current one.
    3124  if(paddingBegin)
    3125  {
    3126  VmaSuballocation paddingSuballoc = {};
    3127  paddingSuballoc.offset = request.offset - paddingBegin;
    3128  paddingSuballoc.size = paddingBegin;
    3129  paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    3130  const VmaSuballocationList::iterator paddingBeginItem =
    3131  m_Suballocations.insert(request.freeSuballocationItem, paddingSuballoc);
    3132  RegisterFreeSuballocation(paddingBeginItem);
    3133  }
    3134 
    3135  // Update totals.
    3136  m_FreeCount = m_FreeCount - 1;
    3137  if(paddingBegin > 0)
    3138  ++m_FreeCount;
    3139  if(paddingEnd > 0)
    3140  ++m_FreeCount;
    3141  m_SumFreeSize -= allocSize;
    3142 }
    3143 
    3144 void VmaBlock::FreeSuballocation(VmaSuballocationList::iterator suballocItem)
    3145 {
    3146  // Change this suballocation to be marked as free.
    3147  VmaSuballocation& suballoc = *suballocItem;
    3148  suballoc.type = VMA_SUBALLOCATION_TYPE_FREE;
    3149 
    3150  // Update totals.
    3151  ++m_FreeCount;
    3152  m_SumFreeSize += suballoc.size;
    3153 
    3154  // Merge with previous and/or next suballocation if it's also free.
    3155  bool mergeWithNext = false;
    3156  bool mergeWithPrev = false;
    3157 
    3158  VmaSuballocationList::iterator nextItem = suballocItem;
    3159  ++nextItem;
    3160  if((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE))
    3161  mergeWithNext = true;
    3162 
    3163  VmaSuballocationList::iterator prevItem = suballocItem;
    3164  if(suballocItem != m_Suballocations.begin())
    3165  {
    3166  --prevItem;
    3167  if(prevItem->type == VMA_SUBALLOCATION_TYPE_FREE)
    3168  mergeWithPrev = true;
    3169  }
    3170 
    3171  if(mergeWithNext)
    3172  {
    3173  UnregisterFreeSuballocation(nextItem);
    3174  MergeFreeWithNext(suballocItem);
    3175  }
    3176 
    3177  if(mergeWithPrev)
    3178  {
    3179  UnregisterFreeSuballocation(prevItem);
    3180  MergeFreeWithNext(prevItem);
    3181  RegisterFreeSuballocation(prevItem);
    3182  }
    3183  else
    3184  RegisterFreeSuballocation(suballocItem);
    3185 }
    3186 
    3187 void VmaBlock::Free(const VmaAllocation allocation)
    3188 {
    3189  const VkDeviceSize allocationOffset = allocation->GetOffset();
    3190  for(VmaSuballocationList::iterator suballocItem = m_Suballocations.begin();
    3191  suballocItem != m_Suballocations.end();
    3192  ++suballocItem)
    3193  {
    3194  VmaSuballocation& suballoc = *suballocItem;
    3195  if(suballoc.offset == allocationOffset)
    3196  {
    3197  FreeSuballocation(suballocItem);
    3198  VMA_HEAVY_ASSERT(Validate());
    3199  return;
    3200  }
    3201  }
    3202  VMA_ASSERT(0 && "Not found!");
    3203 }
    3204 
    3205 #if VMA_STATS_STRING_ENABLED
    3206 
    3207 void VmaBlock::PrintDetailedMap(class VmaStringBuilder& sb) const
    3208 {
    3209  sb.Add("{\n\t\t\t\"Bytes\": ");
    3210  sb.AddNumber(m_Size);
    3211  sb.Add(",\n\t\t\t\"FreeBytes\": ");
    3212  sb.AddNumber(m_SumFreeSize);
    3213  sb.Add(",\n\t\t\t\"Suballocations\": ");
    3214  sb.AddNumber(m_Suballocations.size());
    3215  sb.Add(",\n\t\t\t\"FreeSuballocations\": ");
    3216  sb.AddNumber(m_FreeCount);
    3217  sb.Add(",\n\t\t\t\"SuballocationList\": [");
    3218 
    3219  size_t i = 0;
    3220  for(VmaSuballocationList::const_iterator suballocItem = m_Suballocations.cbegin();
    3221  suballocItem != m_Suballocations.cend();
    3222  ++suballocItem, ++i)
    3223  {
    3224  if(i > 0)
    3225  sb.Add(",\n\t\t\t\t{ \"Type\": ");
    3226  else
    3227  sb.Add("\n\t\t\t\t{ \"Type\": ");
    3228  sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[suballocItem->type]);
    3229  sb.Add(", \"Size\": ");
    3230  sb.AddNumber(suballocItem->size);
    3231  sb.Add(", \"Offset\": ");
    3232  sb.AddNumber(suballocItem->offset);
    3233  sb.Add(" }");
    3234  }
    3235 
    3236  sb.Add("\n\t\t\t]\n\t\t}");
    3237 }
    3238 
    3239 #endif // #if VMA_STATS_STRING_ENABLED
    3240 
    3241 void VmaBlock::MergeFreeWithNext(VmaSuballocationList::iterator item)
    3242 {
    3243  VMA_ASSERT(item != m_Suballocations.end());
    3244  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    3245 
    3246  VmaSuballocationList::iterator nextItem = item;
    3247  ++nextItem;
    3248  VMA_ASSERT(nextItem != m_Suballocations.end());
    3249  VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE);
    3250 
    3251  item->size += nextItem->size;
    3252  --m_FreeCount;
    3253  m_Suballocations.erase(nextItem);
    3254 }
    3255 
    3256 void VmaBlock::RegisterFreeSuballocation(VmaSuballocationList::iterator item)
    3257 {
    3258  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    3259  VMA_ASSERT(item->size > 0);
    3260 
    3261  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    3262  {
    3263  if(m_FreeSuballocationsBySize.empty())
    3264  m_FreeSuballocationsBySize.push_back(item);
    3265  else
    3266  {
    3267  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    3268  m_FreeSuballocationsBySize.data(),
    3269  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
    3270  item,
    3271  VmaSuballocationItemSizeLess());
    3272  size_t index = it - m_FreeSuballocationsBySize.data();
    3273  VectorInsert(m_FreeSuballocationsBySize, index, item);
    3274  }
    3275  }
    3276 }
    3277 
    3278 void VmaBlock::UnregisterFreeSuballocation(VmaSuballocationList::iterator item)
    3279 {
    3280  VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE);
    3281  VMA_ASSERT(item->size > 0);
    3282 
    3283  if(item->size >= VMA_MIN_FREE_SUBALLOCATION_SIZE_TO_REGISTER)
    3284  {
    3285  VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
    3286  m_FreeSuballocationsBySize.data(),
    3287  m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(),
    3288  item,
    3289  VmaSuballocationItemSizeLess());
    3290  for(size_t index = it - m_FreeSuballocationsBySize.data();
    3291  index < m_FreeSuballocationsBySize.size();
    3292  ++index)
    3293  {
    3294  if(m_FreeSuballocationsBySize[index] == item)
    3295  {
    3296  VectorRemove(m_FreeSuballocationsBySize, index);
    3297  return;
    3298  }
    3299  VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found.");
    3300  }
    3301  VMA_ASSERT(0 && "Not found.");
    3302  }
    3303 }
    3304 
    3305 static void InitStatInfo(VmaStatInfo& outInfo)
    3306 {
    3307  memset(&outInfo, 0, sizeof(outInfo));
    3308  outInfo.SuballocationSizeMin = UINT64_MAX;
    3309  outInfo.UnusedRangeSizeMin = UINT64_MAX;
    3310 }
    3311 
    3312 static void CalcAllocationStatInfo(VmaStatInfo& outInfo, const VmaBlock& alloc)
    3313 {
    3314  outInfo.AllocationCount = 1;
    3315 
    3316  const uint32_t rangeCount = (uint32_t)alloc.m_Suballocations.size();
    3317  outInfo.SuballocationCount = rangeCount - alloc.m_FreeCount;
    3318  outInfo.UnusedRangeCount = alloc.m_FreeCount;
    3319 
    3320  outInfo.UnusedBytes = alloc.m_SumFreeSize;
    3321  outInfo.UsedBytes = alloc.m_Size - outInfo.UnusedBytes;
    3322 
    3323  outInfo.SuballocationSizeMin = UINT64_MAX;
    3324  outInfo.SuballocationSizeMax = 0;
    3325  outInfo.UnusedRangeSizeMin = UINT64_MAX;
    3326  outInfo.UnusedRangeSizeMax = 0;
    3327 
    3328  for(VmaSuballocationList::const_iterator suballocItem = alloc.m_Suballocations.cbegin();
    3329  suballocItem != alloc.m_Suballocations.cend();
    3330  ++suballocItem)
    3331  {
    3332  const VmaSuballocation& suballoc = *suballocItem;
    3333  if(suballoc.type != VMA_SUBALLOCATION_TYPE_FREE)
    3334  {
    3335  outInfo.SuballocationSizeMin = VMA_MIN(outInfo.SuballocationSizeMin, suballoc.size);
    3336  outInfo.SuballocationSizeMax = VMA_MAX(outInfo.SuballocationSizeMax, suballoc.size);
    3337  }
    3338  else
    3339  {
    3340  outInfo.UnusedRangeSizeMin = VMA_MIN(outInfo.UnusedRangeSizeMin, suballoc.size);
    3341  outInfo.UnusedRangeSizeMax = VMA_MAX(outInfo.UnusedRangeSizeMax, suballoc.size);
    3342  }
    3343  }
    3344 }
    3345 
    3346 // Adds statistics srcInfo into inoutInfo, like: inoutInfo += srcInfo.
    3347 static void VmaAddStatInfo(VmaStatInfo& inoutInfo, const VmaStatInfo& srcInfo)
    3348 {
    3349  inoutInfo.AllocationCount += srcInfo.AllocationCount;
    3350  inoutInfo.SuballocationCount += srcInfo.SuballocationCount;
    3351  inoutInfo.UnusedRangeCount += srcInfo.UnusedRangeCount;
    3352  inoutInfo.UsedBytes += srcInfo.UsedBytes;
    3353  inoutInfo.UnusedBytes += srcInfo.UnusedBytes;
    3354  inoutInfo.SuballocationSizeMin = VMA_MIN(inoutInfo.SuballocationSizeMin, srcInfo.SuballocationSizeMin);
    3355  inoutInfo.SuballocationSizeMax = VMA_MAX(inoutInfo.SuballocationSizeMax, srcInfo.SuballocationSizeMax);
    3356  inoutInfo.UnusedRangeSizeMin = VMA_MIN(inoutInfo.UnusedRangeSizeMin, srcInfo.UnusedRangeSizeMin);
    3357  inoutInfo.UnusedRangeSizeMax = VMA_MAX(inoutInfo.UnusedRangeSizeMax, srcInfo.UnusedRangeSizeMax);
    3358 }
    3359 
    3360 static void VmaPostprocessCalcStatInfo(VmaStatInfo& inoutInfo)
    3361 {
    3362  inoutInfo.SuballocationSizeAvg = (inoutInfo.SuballocationCount > 0) ?
    3363  VmaRoundDiv<VkDeviceSize>(inoutInfo.UsedBytes, inoutInfo.SuballocationCount) : 0;
    3364  inoutInfo.UnusedRangeSizeAvg = (inoutInfo.UnusedRangeCount > 0) ?
    3365  VmaRoundDiv<VkDeviceSize>(inoutInfo.UnusedBytes, inoutInfo.UnusedRangeCount) : 0;
    3366 }
    3367 
    3368 VmaBlockVector::VmaBlockVector(VmaAllocator hAllocator) :
    3369  m_hAllocator(hAllocator),
    3370  m_Blocks(VmaStlAllocator<VmaBlock*>(hAllocator->GetAllocationCallbacks()))
    3371 {
    3372 }
    3373 
    3374 VmaBlockVector::~VmaBlockVector()
    3375 {
    3376  for(size_t i = m_Blocks.size(); i--; )
    3377  {
    3378  m_Blocks[i]->Destroy(m_hAllocator);
    3379  vma_delete(m_hAllocator, m_Blocks[i]);
    3380  }
    3381 }
    3382 
    3383 void VmaBlockVector::Remove(VmaBlock* pBlock)
    3384 {
    3385  for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
    3386  {
    3387  if(m_Blocks[blockIndex] == pBlock)
    3388  {
    3389  VectorRemove(m_Blocks, blockIndex);
    3390  return;
    3391  }
    3392  }
    3393  VMA_ASSERT(0);
    3394 }
    3395 
    3396 void VmaBlockVector::IncrementallySortBlocks()
    3397 {
    3398  // Bubble sort only until first swap.
    3399  for(size_t i = 1; i < m_Blocks.size(); ++i)
    3400  {
    3401  if(m_Blocks[i - 1]->m_SumFreeSize > m_Blocks[i]->m_SumFreeSize)
    3402  {
    3403  VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
    3404  return;
    3405  }
    3406  }
    3407 }
    3408 
    3409 #if VMA_STATS_STRING_ENABLED
    3410 
    3411 void VmaBlockVector::PrintDetailedMap(class VmaStringBuilder& sb) const
    3412 {
    3413  for(size_t i = 0; i < m_Blocks.size(); ++i)
    3414  {
    3415  if(i > 0)
    3416  sb.Add(",\n\t\t");
    3417  else
    3418  sb.Add("\n\t\t");
    3419  m_Blocks[i]->PrintDetailedMap(sb);
    3420  }
    3421 }
    3422 
    3423 #endif // #if VMA_STATS_STRING_ENABLED
    3424 
    3425 void VmaBlockVector::UnmapPersistentlyMappedMemory()
    3426 {
    3427  for(size_t i = m_Blocks.size(); i--; )
    3428  {
    3429  VmaBlock* pBlock = m_Blocks[i];
    3430  if(pBlock->m_pMappedData != VMA_NULL)
    3431  {
    3432  VMA_ASSERT(pBlock->m_PersistentMap != false);
    3433  vkUnmapMemory(m_hAllocator->m_hDevice, pBlock->m_hMemory);
    3434  pBlock->m_pMappedData = VMA_NULL;
    3435  }
    3436  }
    3437 }
    3438 
    3439 VkResult VmaBlockVector::MapPersistentlyMappedMemory()
    3440 {
    3441  VkResult finalResult = VK_SUCCESS;
    3442  for(size_t i = 0, count = m_Blocks.size(); i < count; ++i)
    3443  {
    3444  VmaBlock* pBlock = m_Blocks[i];
    3445  if(pBlock->m_PersistentMap)
    3446  {
    3447  VMA_ASSERT(pBlock->m_pMappedData == nullptr);
    3448  VkResult localResult = vkMapMemory(m_hAllocator->m_hDevice, pBlock->m_hMemory, 0, VK_WHOLE_SIZE, 0, &pBlock->m_pMappedData);
    3449  if(localResult != VK_SUCCESS)
    3450  {
    3451  finalResult = localResult;
    3452  }
    3453  }
    3454  }
    3455  return finalResult;
    3456 }
    3457 
    3458 void VmaBlockVector::AddStats(VmaStats* pStats, uint32_t memTypeIndex, uint32_t memHeapIndex) const
    3459 {
    3460  for(uint32_t allocIndex = 0; allocIndex < m_Blocks.size(); ++allocIndex)
    3461  {
    3462  const VmaBlock* const pBlock = m_Blocks[allocIndex];
    3463  VMA_ASSERT(pBlock);
    3464  VMA_HEAVY_ASSERT(pBlock->Validate());
    3465  VmaStatInfo allocationStatInfo;
    3466  CalcAllocationStatInfo(allocationStatInfo, *pBlock);
    3467  VmaAddStatInfo(pStats->total, allocationStatInfo);
    3468  VmaAddStatInfo(pStats->memoryType[memTypeIndex], allocationStatInfo);
    3469  VmaAddStatInfo(pStats->memoryHeap[memHeapIndex], allocationStatInfo);
    3470  }
    3471 }
    3472 
    3474 // VmaDefragmentator
    3475 
    3476 class VmaDefragmentator
    3477 {
    3478  VkDevice m_hDevice;
    3479  const VkAllocationCallbacks* m_pAllocationCallbacks;
    3480  VkDeviceSize m_BufferImageGranularity;
    3481  uint32_t m_MemTypeIndex;
    3482  VMA_BLOCK_VECTOR_TYPE m_BlockVectorType;
    3483  VkDeviceSize m_BytesMoved;
    3484  uint32_t m_AllocationsMoved;
    3485 
    3486  struct AllocationInfo
    3487  {
    3488  VmaAllocation m_hAllocation;
    3489  VkBool32* m_pChanged;
    3490 
    3491  AllocationInfo() :
    3492  m_hAllocation(VK_NULL_HANDLE),
    3493  m_pChanged(VMA_NULL)
    3494  {
    3495  }
    3496  };
    3497 
    3498  struct AllocationInfoSizeGreater
    3499  {
    3500  bool operator()(const AllocationInfo& lhs, const AllocationInfo& rhs) const
    3501  {
    3502  return lhs.m_hAllocation->GetSize() > rhs.m_hAllocation->GetSize();
    3503  }
    3504  };
    3505 
    3506  // Used between AddAllocation and Defragment.
    3507  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
    3508 
    3509  struct BlockInfo
    3510  {
    3511  VmaBlock* m_pBlock;
    3512  bool m_HasNonMovableAllocations;
    3513  VmaVector< AllocationInfo, VmaStlAllocator<AllocationInfo> > m_Allocations;
    3514 
    3515  BlockInfo(const VkAllocationCallbacks* pAllocationCallbacks) :
    3516  m_pBlock(VMA_NULL),
    3517  m_HasNonMovableAllocations(true),
    3518  m_Allocations(pAllocationCallbacks),
    3519  m_pMappedDataForDefragmentation(VMA_NULL)
    3520  {
    3521  }
    3522 
    3523  void CalcHasNonMovableAllocations()
    3524  {
    3525  const size_t blockAllocCount =
    3526  m_pBlock->m_Suballocations.size() - m_pBlock->m_FreeCount;
    3527  const size_t defragmentAllocCount = m_Allocations.size();
    3528  m_HasNonMovableAllocations = blockAllocCount != defragmentAllocCount;
    3529  }
    3530 
    3531  void SortAllocationsBySizeDescecnding()
    3532  {
    3533  VMA_SORT(m_Allocations.begin(), m_Allocations.end(), AllocationInfoSizeGreater());
    3534  }
    3535 
    3536  VkResult EnsureMapping(VkDevice hDevice, void** ppMappedData)
    3537  {
    3538  // It has already been mapped for defragmentation.
    3539  if(m_pMappedDataForDefragmentation)
    3540  {
    3541  *ppMappedData = m_pMappedDataForDefragmentation;
    3542  return VK_SUCCESS;
    3543  }
    3544 
    3545  // It is persistently mapped.
    3546  if(m_pBlock->m_PersistentMap)
    3547  {
    3548  VMA_ASSERT(m_pBlock->m_pMappedData != VMA_NULL);
    3549  *ppMappedData = m_pBlock->m_pMappedData;
    3550  return VK_SUCCESS;
    3551  }
    3552 
    3553  // Map on first usage.
    3554  VkResult res = vkMapMemory(hDevice, m_pBlock->m_hMemory, 0, VK_WHOLE_SIZE, 0, &m_pMappedDataForDefragmentation);
    3555  *ppMappedData = m_pMappedDataForDefragmentation;
    3556  return res;
    3557  }
    3558 
    3559  void Unmap(VkDevice hDevice)
    3560  {
    3561  if(m_pMappedDataForDefragmentation != VMA_NULL)
    3562  {
    3563  vkUnmapMemory(hDevice, m_pBlock->m_hMemory);
    3564  }
    3565  }
    3566 
    3567  private:
    3568  // Not null if mapped for defragmentation only, not persistently mapped.
    3569  void* m_pMappedDataForDefragmentation;
    3570  };
    3571 
    3572  struct BlockPointerLess
    3573  {
    3574  bool operator()(const BlockInfo* pLhsBlockInfo, const VmaBlock* pRhsBlock) const
    3575  {
    3576  return pLhsBlockInfo->m_pBlock < pRhsBlock;
    3577  }
    3578  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
    3579  {
    3580  return pLhsBlockInfo->m_pBlock < pRhsBlockInfo->m_pBlock;
    3581  }
    3582  };
    3583 
    3584  // 1. Blocks with some non-movable allocations go first.
    3585  // 2. Blocks with smaller sumFreeSize go first.
    3586  struct BlockInfoCompareMoveDestination
    3587  {
    3588  bool operator()(const BlockInfo* pLhsBlockInfo, const BlockInfo* pRhsBlockInfo) const
    3589  {
    3590  if(pLhsBlockInfo->m_HasNonMovableAllocations && !pRhsBlockInfo->m_HasNonMovableAllocations)
    3591  return true;
    3592  if(!pLhsBlockInfo->m_HasNonMovableAllocations && pRhsBlockInfo->m_HasNonMovableAllocations)
    3593  return false;
    3594  if(pLhsBlockInfo->m_pBlock->m_SumFreeSize < pRhsBlockInfo->m_pBlock->m_SumFreeSize)
    3595  return true;
    3596  return false;
    3597  }
    3598  };
    3599 
    3600  typedef VmaVector< BlockInfo*, VmaStlAllocator<BlockInfo*> > BlockInfoVector;
    3601  BlockInfoVector m_Blocks;
    3602 
    3603  VkResult DefragmentRound(
    3604  VkDeviceSize maxBytesToMove,
    3605  uint32_t maxAllocationsToMove);
    3606 
    3607  static bool MoveMakesSense(
    3608  size_t dstBlockIndex, VkDeviceSize dstOffset,
    3609  size_t srcBlockIndex, VkDeviceSize srcOffset);
    3610 
    3611 public:
    3612  VmaDefragmentator(
    3613  VkDevice hDevice,
    3614  const VkAllocationCallbacks* pAllocationCallbacks,
    3615  VkDeviceSize bufferImageGranularity,
    3616  uint32_t memTypeIndex,
    3617  VMA_BLOCK_VECTOR_TYPE blockVectorType);
    3618 
    3619  ~VmaDefragmentator();
    3620 
    3621  VkDeviceSize GetBytesMoved() const { return m_BytesMoved; }
    3622  uint32_t GetAllocationsMoved() const { return m_AllocationsMoved; }
    3623 
    3624  void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
    3625 
    3626  VkResult Defragment(
    3627  VmaBlockVector* pBlockVector,
    3628  VkDeviceSize maxBytesToMove,
    3629  uint32_t maxAllocationsToMove);
    3630 };
    3631 
    3632 VmaDefragmentator::VmaDefragmentator(
    3633  VkDevice hDevice,
    3634  const VkAllocationCallbacks* pAllocationCallbacks,
    3635  VkDeviceSize bufferImageGranularity,
    3636  uint32_t memTypeIndex,
    3637  VMA_BLOCK_VECTOR_TYPE blockVectorType) :
    3638  m_hDevice(hDevice),
    3639  m_pAllocationCallbacks(pAllocationCallbacks),
    3640  m_BufferImageGranularity(bufferImageGranularity),
    3641  m_MemTypeIndex(memTypeIndex),
    3642  m_BlockVectorType(blockVectorType),
    3643  m_BytesMoved(0),
    3644  m_AllocationsMoved(0),
    3645  m_Allocations(VmaStlAllocator<AllocationInfo>(pAllocationCallbacks)),
    3646  m_Blocks(VmaStlAllocator<BlockInfo*>(pAllocationCallbacks))
    3647 {
    3648 }
    3649 
    3650 VmaDefragmentator::~VmaDefragmentator()
    3651 {
    3652  for(size_t i = m_Blocks.size(); i--; )
    3653  {
    3654  vma_delete(m_pAllocationCallbacks, m_Blocks[i]);
    3655  }
    3656 }
    3657 
    3658 void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
    3659 {
    3660  AllocationInfo allocInfo;
    3661  allocInfo.m_hAllocation = hAlloc;
    3662  allocInfo.m_pChanged = pChanged;
    3663  m_Allocations.push_back(allocInfo);
    3664 }
    3665 
    3666 VkResult VmaDefragmentator::DefragmentRound(
    3667  VkDeviceSize maxBytesToMove,
    3668  uint32_t maxAllocationsToMove)
    3669 {
    3670  if(m_Blocks.empty())
    3671  {
    3672  return VK_SUCCESS;
    3673  }
    3674 
    3675  size_t srcBlockIndex = m_Blocks.size() - 1;
    3676  size_t srcAllocIndex = SIZE_MAX;
    3677  for(;;)
    3678  {
    3679  // 1. Find next allocation to move.
    3680  // 1.1. Start from last to first m_Blocks - they are sorted from most "destination" to most "source".
    3681  // 1.2. Then start from last to first m_Allocations - they are sorted from largest to smallest.
    3682  while(srcAllocIndex >= m_Blocks[srcBlockIndex]->m_Allocations.size())
    3683  {
    3684  if(m_Blocks[srcBlockIndex]->m_Allocations.empty())
    3685  {
    3686  // Finished: no more allocations to process.
    3687  if(srcBlockIndex == 0)
    3688  {
    3689  return VK_SUCCESS;
    3690  }
    3691  else
    3692  {
    3693  --srcBlockIndex;
    3694  srcAllocIndex = SIZE_MAX;
    3695  }
    3696  }
    3697  else
    3698  {
    3699  srcAllocIndex = m_Blocks[srcBlockIndex]->m_Allocations.size() - 1;
    3700  }
    3701  }
    3702 
    3703  BlockInfo* pSrcBlockInfo = m_Blocks[srcBlockIndex];
    3704  AllocationInfo& allocInfo = pSrcBlockInfo->m_Allocations[srcAllocIndex];
    3705 
    3706  const VkDeviceSize size = allocInfo.m_hAllocation->GetSize();
    3707  const VkDeviceSize srcOffset = allocInfo.m_hAllocation->GetOffset();
    3708  const VkDeviceSize alignment = allocInfo.m_hAllocation->GetAlignment();
    3709  const VmaSuballocationType suballocType = allocInfo.m_hAllocation->GetSuballocationType();
    3710 
    3711  // 2. Try to find new place for this allocation in preceding or current block.
    3712  for(size_t dstBlockIndex = 0; dstBlockIndex <= srcBlockIndex; ++dstBlockIndex)
    3713  {
    3714  BlockInfo* pDstBlockInfo = m_Blocks[dstBlockIndex];
    3715  VmaAllocationRequest dstAllocRequest;
    3716  if(pDstBlockInfo->m_pBlock->CreateAllocationRequest(
    3717  m_BufferImageGranularity,
    3718  size,
    3719  alignment,
    3720  suballocType,
    3721  &dstAllocRequest) &&
    3722  MoveMakesSense(
    3723  dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))
    3724  {
    3725  // Reached limit on number of allocations or bytes to move.
    3726  if((m_AllocationsMoved + 1 > maxAllocationsToMove) ||
    3727  (m_BytesMoved + size > maxBytesToMove))
    3728  {
    3729  return VK_INCOMPLETE;
    3730  }
    3731 
    3732  void* pDstMappedData = VMA_NULL;
    3733  VkResult res = pDstBlockInfo->EnsureMapping(m_hDevice, &pDstMappedData);
    3734  if(res != VK_SUCCESS)
    3735  {
    3736  return res;
    3737  }
    3738 
    3739  void* pSrcMappedData = VMA_NULL;
    3740  res = pSrcBlockInfo->EnsureMapping(m_hDevice, &pSrcMappedData);
    3741  if(res != VK_SUCCESS)
    3742  {
    3743  return res;
    3744  }
    3745 
    3746  // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
    3747  memcpy(
    3748  reinterpret_cast<char*>(pDstMappedData) + dstAllocRequest.offset,
    3749  reinterpret_cast<char*>(pSrcMappedData) + srcOffset,
    3750  size);
    3751 
    3752  pDstBlockInfo->m_pBlock->Alloc(dstAllocRequest, suballocType, size);
    3753  pSrcBlockInfo->m_pBlock->Free(allocInfo.m_hAllocation);
    3754 
    3755  allocInfo.m_hAllocation->ChangeBlockAllocation(pDstBlockInfo->m_pBlock, dstAllocRequest.offset);
    3756 
    3757  if(allocInfo.m_pChanged != VMA_NULL)
    3758  {
    3759  *allocInfo.m_pChanged = VK_TRUE;
    3760  }
    3761 
    3762  ++m_AllocationsMoved;
    3763  m_BytesMoved += size;
    3764 
    3765  VectorRemove(pSrcBlockInfo->m_Allocations, srcAllocIndex);
    3766 
    3767  break;
    3768  }
    3769  }
    3770 
    3771  // If not processed, this allocInfo remains in pBlockInfo->m_Allocations for next round.
    3772 
    3773  if(srcAllocIndex > 0)
    3774  {
    3775  --srcAllocIndex;
    3776  }
    3777  else
    3778  {
    3779  if(srcBlockIndex > 0)
    3780  {
    3781  --srcBlockIndex;
    3782  srcAllocIndex = SIZE_MAX;
    3783  }
    3784  else
    3785  {
    3786  return VK_SUCCESS;
    3787  }
    3788  }
    3789  }
    3790 }
    3791 
    3792 VkResult VmaDefragmentator::Defragment(
    3793  VmaBlockVector* pBlockVector,
    3794  VkDeviceSize maxBytesToMove,
    3795  uint32_t maxAllocationsToMove)
    3796 {
    3797  if(m_Allocations.empty())
    3798  {
    3799  return VK_SUCCESS;
    3800  }
    3801 
    3802  // Create block info for each block.
    3803  const size_t blockCount = pBlockVector->m_Blocks.size();
    3804  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
    3805  {
    3806  BlockInfo* pBlockInfo = vma_new(m_pAllocationCallbacks, BlockInfo)(m_pAllocationCallbacks);
    3807  pBlockInfo->m_pBlock = pBlockVector->m_Blocks[blockIndex];
    3808  m_Blocks.push_back(pBlockInfo);
    3809  }
    3810 
    3811  // Sort them by m_pBlock pointer value.
    3812  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockPointerLess());
    3813 
    3814  // Move allocation infos from m_Allocations to appropriate m_Blocks[i].m_Allocations.
    3815  for(size_t allocIndex = 0, allocCount = m_Allocations.size(); allocIndex < allocCount; ++allocIndex)
    3816  {
    3817  AllocationInfo& allocInfo = m_Allocations[allocIndex];
    3818  VmaBlock* pBlock = allocInfo.m_hAllocation->GetBlock();
    3819  BlockInfoVector::iterator it = VmaBinaryFindFirstNotLess(m_Blocks.begin(), m_Blocks.end(), pBlock, BlockPointerLess());
    3820  if(it != m_Blocks.end() && (*it)->m_pBlock == pBlock)
    3821  {
    3822  (*it)->m_Allocations.push_back(allocInfo);
    3823  }
    3824  else
    3825  {
    3826  VMA_ASSERT(0);
    3827  }
    3828  }
    3829  m_Allocations.clear();
    3830 
    3831  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
    3832  {
    3833  BlockInfo* pBlockInfo = m_Blocks[blockIndex];
    3834  pBlockInfo->CalcHasNonMovableAllocations();
    3835  pBlockInfo->SortAllocationsBySizeDescecnding();
    3836  }
    3837 
    3838  // Sort m_Blocks this time by the main criterium, from most "destination" to most "source" blocks.
    3839  VMA_SORT(m_Blocks.begin(), m_Blocks.end(), BlockInfoCompareMoveDestination());
    3840 
    3841  // Execute defragmentation round (the main part).
    3842  VkResult result = VK_SUCCESS;
    3843  for(size_t round = 0; (round < 2) && (result == VK_SUCCESS); ++round)
    3844  {
    3845  result = DefragmentRound(maxBytesToMove, maxAllocationsToMove);
    3846  }
    3847 
    3848  // Unmap blocks that were mapped for defragmentation.
    3849  for(size_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
    3850  {
    3851  m_Blocks[blockIndex]->Unmap(m_hDevice);
    3852  }
    3853 
    3854  return result;
    3855 }
    3856 
    3857 bool VmaDefragmentator::MoveMakesSense(
    3858  size_t dstBlockIndex, VkDeviceSize dstOffset,
    3859  size_t srcBlockIndex, VkDeviceSize srcOffset)
    3860 {
    3861  if(dstBlockIndex < srcBlockIndex)
    3862  {
    3863  return true;
    3864  }
    3865  if(dstBlockIndex > srcBlockIndex)
    3866  {
    3867  return false;
    3868  }
    3869  if(dstOffset < srcOffset)
    3870  {
    3871  return true;
    3872  }
    3873  return false;
    3874 }
    3875 
    3877 // VmaAllocator_T
    3878 
    3879 VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) :
    3880  m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_EXTERNALLY_SYNCHRONIZED_BIT) == 0),
    3881  m_PhysicalDevice(pCreateInfo->physicalDevice),
    3882  m_hDevice(pCreateInfo->device),
    3883  m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL),
    3884  m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ?
    3885  *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks),
    3886  m_PreferredLargeHeapBlockSize(0),
    3887  m_PreferredSmallHeapBlockSize(0),
    3888  m_UnmapPersistentlyMappedMemoryCounter(0)
    3889 {
    3890  VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device);
    3891 
    3892  memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks));
    3893  memset(&m_MemProps, 0, sizeof(m_MemProps));
    3894  memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties));
    3895 
    3896  memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors));
    3897  memset(&m_HasEmptyBlock, 0, sizeof(m_HasEmptyBlock));
    3898  memset(&m_pOwnAllocations, 0, sizeof(m_pOwnAllocations));
    3899 
    3900  if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL)
    3901  {
    3902  m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate;
    3903  m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree;
    3904  }
    3905 
    3906  m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ?
    3907  pCreateInfo->preferredLargeHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE);
    3908  m_PreferredSmallHeapBlockSize = (pCreateInfo->preferredSmallHeapBlockSize != 0) ?
    3909  pCreateInfo->preferredSmallHeapBlockSize : static_cast<VkDeviceSize>(VMA_DEFAULT_SMALL_HEAP_BLOCK_SIZE);
    3910 
    3911  vkGetPhysicalDeviceProperties(m_PhysicalDevice, &m_PhysicalDeviceProperties);
    3912  vkGetPhysicalDeviceMemoryProperties(m_PhysicalDevice, &m_MemProps);
    3913 
    3914  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
    3915  {
    3916  for(size_t j = 0; j < VMA_BLOCK_VECTOR_TYPE_COUNT; ++j)
    3917  {
    3918  m_pBlockVectors[i][j] = vma_new(this, VmaBlockVector)(this);
    3919  m_pOwnAllocations[i][j] = vma_new(this, AllocationVectorType)(VmaStlAllocator<VmaAllocation>(GetAllocationCallbacks()));
    3920  }
    3921  }
    3922 }
    3923 
    3924 VmaAllocator_T::~VmaAllocator_T()
    3925 {
    3926  for(uint32_t typeIndex = 0; typeIndex < GetMemoryTypeCount(); ++typeIndex)
    3927  {
    3928  for(size_t blockVectorType = VMA_BLOCK_VECTOR_TYPE_COUNT; blockVectorType--; )
    3929  {
    3930  AllocationVectorType* pOwnAllocations = m_pOwnAllocations[typeIndex][blockVectorType];
    3931  VMA_ASSERT(pOwnAllocations != VMA_NULL && pOwnAllocations->size() == 0);
    3932  }
    3933  }
    3934 
    3935  for(size_t i = GetMemoryTypeCount(); i--; )
    3936  {
    3937  for(size_t j = VMA_BLOCK_VECTOR_TYPE_COUNT; j--; )
    3938  {
    3939  vma_delete(this, m_pOwnAllocations[i][j]);
    3940  vma_delete(this, m_pBlockVectors[i][j]);
    3941  }
    3942  }
    3943 }
    3944 
    3945 VkDeviceSize VmaAllocator_T::GetPreferredBlockSize(uint32_t memTypeIndex) const
    3946 {
    3947  VkDeviceSize heapSize = m_MemProps.memoryHeaps[m_MemProps.memoryTypes[memTypeIndex].heapIndex].size;
    3948  return (heapSize <= VMA_SMALL_HEAP_MAX_SIZE) ?
    3949  m_PreferredSmallHeapBlockSize : m_PreferredLargeHeapBlockSize;
    3950 }
    3951 
    3952 VkResult VmaAllocator_T::AllocateMemoryOfType(
    3953  const VkMemoryRequirements& vkMemReq,
    3954  const VmaMemoryRequirements& vmaMemReq,
    3955  uint32_t memTypeIndex,
    3956  VmaSuballocationType suballocType,
    3957  VmaAllocation* pAllocation)
    3958 {
    3959  VMA_ASSERT(pAllocation != VMA_NULL);
    3960  VMA_DEBUG_LOG(" AllocateMemory: MemoryTypeIndex=%u, Size=%llu", memTypeIndex, vkMemReq.size);
    3961 
    3962  const VkDeviceSize preferredBlockSize = GetPreferredBlockSize(memTypeIndex);
    3963  // Heuristics: Allocate own memory if requested size if greater than half of preferred block size.
    3964  const bool ownMemory =
    3965  (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT) != 0 ||
    3966  VMA_DEBUG_ALWAYS_OWN_MEMORY ||
    3967  ((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) == 0 &&
    3968  vkMemReq.size > preferredBlockSize / 2);
    3969 
    3970  if(ownMemory)
    3971  {
    3972  if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) != 0)
    3973  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    3974  else
    3975  {
    3976  return AllocateOwnMemory(
    3977  vkMemReq.size,
    3978  suballocType,
    3979  memTypeIndex,
    3981  vmaMemReq.pUserData,
    3982  pAllocation);
    3983  }
    3984  }
    3985  else
    3986  {
    3987  uint32_t blockVectorType = VmaMemoryRequirementFlagsToBlockVectorType(vmaMemReq.flags);
    3988 
    3989  VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    3990  VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex][blockVectorType];
    3991  VMA_ASSERT(blockVector);
    3992 
    3993  // 1. Search existing allocations.
    3994  // Forward order - prefer blocks with smallest amount of free space.
    3995  for(size_t allocIndex = 0; allocIndex < blockVector->m_Blocks.size(); ++allocIndex )
    3996  {
    3997  VmaBlock* const pBlock = blockVector->m_Blocks[allocIndex];
    3998  VMA_ASSERT(pBlock);
    3999  VmaAllocationRequest allocRequest = {};
    4000  // Check if can allocate from pBlock.
    4001  if(pBlock->CreateAllocationRequest(
    4002  GetBufferImageGranularity(),
    4003  vkMemReq.size,
    4004  vkMemReq.alignment,
    4005  suballocType,
    4006  &allocRequest))
    4007  {
    4008  // We no longer have an empty Allocation.
    4009  if(pBlock->IsEmpty())
    4010  m_HasEmptyBlock[memTypeIndex] = false;
    4011  // Allocate from this pBlock.
    4012  pBlock->Alloc(allocRequest, suballocType, vkMemReq.size);
    4013  *pAllocation = vma_new(this, VmaAllocation_T)();
    4014  (*pAllocation)->InitBlockAllocation(
    4015  pBlock,
    4016  allocRequest.offset,
    4017  vkMemReq.alignment,
    4018  vkMemReq.size,
    4019  suballocType,
    4020  vmaMemReq.pUserData);
    4021  VMA_HEAVY_ASSERT(pBlock->Validate());
    4022  VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)allocIndex);
    4023  return VK_SUCCESS;
    4024  }
    4025  }
    4026 
    4027  // 2. Create new Allocation.
    4028  if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT) != 0)
    4029  {
    4030  VMA_DEBUG_LOG(" FAILED due to VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT");
    4031  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    4032  }
    4033  else
    4034  {
    4035  // Start with full preferredBlockSize.
    4036  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
    4037  allocInfo.memoryTypeIndex = memTypeIndex;
    4038  allocInfo.allocationSize = preferredBlockSize;
    4039  VkDeviceMemory mem = VK_NULL_HANDLE;
    4040  VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    4041  if(res < 0)
    4042  {
    4043  // 3. Try half the size.
    4044  allocInfo.allocationSize /= 2;
    4045  if(allocInfo.allocationSize >= vkMemReq.size)
    4046  {
    4047  res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    4048  if(res < 0)
    4049  {
    4050  // 4. Try quarter the size.
    4051  allocInfo.allocationSize /= 2;
    4052  if(allocInfo.allocationSize >= vkMemReq.size)
    4053  {
    4054  res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &mem);
    4055  }
    4056  }
    4057  }
    4058  }
    4059  if(res < 0)
    4060  {
    4061  // 5. Try OwnAlloc.
    4062  res = AllocateOwnMemory(
    4063  vkMemReq.size,
    4064  suballocType,
    4065  memTypeIndex,
    4067  vmaMemReq.pUserData,
    4068  pAllocation);
    4069  if(res == VK_SUCCESS)
    4070  {
    4071  // Succeeded: AllocateOwnMemory function already filld pMemory, nothing more to do here.
    4072  VMA_DEBUG_LOG(" Allocated as OwnMemory");
    4073  return VK_SUCCESS;
    4074  }
    4075  else
    4076  {
    4077  // Everything failed: Return error code.
    4078  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
    4079  return res;
    4080  }
    4081  }
    4082 
    4083  // New VkDeviceMemory successfully created.
    4084 
    4085  // Map memory if needed.
    4086  void* pMappedData = VMA_NULL;
    4087  const bool persistentMap = (vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0;
    4088  if(persistentMap && m_UnmapPersistentlyMappedMemoryCounter == 0)
    4089  {
    4090  res = vkMapMemory(m_hDevice, mem, 0, VK_WHOLE_SIZE, 0, &pMappedData);
    4091  if(res < 0)
    4092  {
    4093  VMA_DEBUG_LOG(" vkMapMemory FAILED");
    4094  vkFreeMemory(m_hDevice, mem, GetAllocationCallbacks());
    4095  return res;
    4096  }
    4097  }
    4098 
    4099  // Callback.
    4100  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
    4101  {
    4102  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, memTypeIndex, mem, allocInfo.allocationSize);
    4103  }
    4104 
    4105  // Create new Allocation for it.
    4106  VmaBlock* const pBlock = vma_new(this, VmaBlock)(this);
    4107  pBlock->Init(
    4108  memTypeIndex,
    4109  (VMA_BLOCK_VECTOR_TYPE)blockVectorType,
    4110  mem,
    4111  allocInfo.allocationSize,
    4112  persistentMap,
    4113  pMappedData);
    4114 
    4115  blockVector->m_Blocks.push_back(pBlock);
    4116 
    4117  // Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled.
    4118  VmaAllocationRequest allocRequest = {};
    4119  allocRequest.freeSuballocationItem = pBlock->m_Suballocations.begin();
    4120  allocRequest.offset = 0;
    4121  pBlock->Alloc(allocRequest, suballocType, vkMemReq.size);
    4122  *pAllocation = vma_new(this, VmaAllocation_T)();
    4123  (*pAllocation)->InitBlockAllocation(
    4124  pBlock,
    4125  allocRequest.offset,
    4126  vkMemReq.alignment,
    4127  vkMemReq.size,
    4128  suballocType,
    4129  vmaMemReq.pUserData);
    4130  VMA_HEAVY_ASSERT(pBlock->Validate());
    4131  VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
    4132  return VK_SUCCESS;
    4133  }
    4134  }
    4135 }
    4136 
    4137 VkResult VmaAllocator_T::AllocateOwnMemory(
    4138  VkDeviceSize size,
    4139  VmaSuballocationType suballocType,
    4140  uint32_t memTypeIndex,
    4141  bool map,
    4142  void* pUserData,
    4143  VmaAllocation* pAllocation)
    4144 {
    4145  VMA_ASSERT(pAllocation);
    4146 
    4147  VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
    4148  allocInfo.memoryTypeIndex = memTypeIndex;
    4149  allocInfo.allocationSize = size;
    4150 
    4151  // Allocate VkDeviceMemory.
    4152  VkDeviceMemory hMemory = VK_NULL_HANDLE;
    4153  VkResult res = vkAllocateMemory(m_hDevice, &allocInfo, GetAllocationCallbacks(), &hMemory);
    4154  if(res < 0)
    4155  {
    4156  VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
    4157  return res;
    4158  }
    4159 
    4160  void* pMappedData = nullptr;
    4161  if(map)
    4162  {
    4163  if(m_UnmapPersistentlyMappedMemoryCounter == 0)
    4164  {
    4165  res = vkMapMemory(m_hDevice, hMemory, 0, VK_WHOLE_SIZE, 0, &pMappedData);
    4166  if(res < 0)
    4167  {
    4168  VMA_DEBUG_LOG(" vkMapMemory FAILED");
    4169  vkFreeMemory(m_hDevice, hMemory, GetAllocationCallbacks());
    4170  return res;
    4171  }
    4172  }
    4173  }
    4174 
    4175  // Callback.
    4176  if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL)
    4177  {
    4178  (*m_DeviceMemoryCallbacks.pfnAllocate)(this, memTypeIndex, hMemory, size);
    4179  }
    4180 
    4181  *pAllocation = vma_new(this, VmaAllocation_T)();
    4182  (*pAllocation)->InitOwnAllocation(memTypeIndex, hMemory, suballocType, map, pMappedData, size, pUserData);
    4183 
    4184  // Register it in m_pOwnAllocations.
    4185  {
    4186  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex);
    4187  AllocationVectorType* pOwnAllocations = m_pOwnAllocations[memTypeIndex][map ? VMA_BLOCK_VECTOR_TYPE_MAPPED : VMA_BLOCK_VECTOR_TYPE_UNMAPPED];
    4188  VMA_ASSERT(pOwnAllocations);
    4189  VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data();
    4190  VmaAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size();
    4191  const size_t indexToInsert = VmaBinaryFindFirstNotLess(
    4192  pOwnAllocationsBeg,
    4193  pOwnAllocationsEnd,
    4194  *pAllocation,
    4195  VmaPointerLess()) - pOwnAllocationsBeg;
    4196  VectorInsert(*pOwnAllocations, indexToInsert, *pAllocation);
    4197  }
    4198 
    4199  VMA_DEBUG_LOG(" Allocated OwnMemory MemoryTypeIndex=#%u", memTypeIndex);
    4200 
    4201  return VK_SUCCESS;
    4202 }
    4203 
    4204 VkResult VmaAllocator_T::AllocateMemory(
    4205  const VkMemoryRequirements& vkMemReq,
    4206  const VmaMemoryRequirements& vmaMemReq,
    4207  VmaSuballocationType suballocType,
    4208  VmaAllocation* pAllocation)
    4209 {
    4210  if((vmaMemReq.flags & VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT) != 0 &&
    4212  {
    4213  VMA_ASSERT(0 && "Specifying VMA_MEMORY_REQUIREMENT_OWN_MEMORY_BIT together with VMA_MEMORY_REQUIREMENT_NEVER_ALLOCATE_BIT makes no sense.");
    4214  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    4215  }
    4216 
    4217  // Bit mask of memory Vulkan types acceptable for this allocation.
    4218  uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
    4219  uint32_t memTypeIndex = UINT32_MAX;
    4220  VkResult res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex);
    4221  if(res == VK_SUCCESS)
    4222  {
    4223  res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pAllocation);
    4224  // Succeeded on first try.
    4225  if(res == VK_SUCCESS)
    4226  {
    4227  return res;
    4228  }
    4229  // Allocation from this memory type failed. Try other compatible memory types.
    4230  else
    4231  {
    4232  for(;;)
    4233  {
    4234  // Remove old memTypeIndex from list of possibilities.
    4235  memoryTypeBits &= ~(1u << memTypeIndex);
    4236  // Find alternative memTypeIndex.
    4237  res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &vmaMemReq, &memTypeIndex);
    4238  if(res == VK_SUCCESS)
    4239  {
    4240  res = AllocateMemoryOfType(vkMemReq, vmaMemReq, memTypeIndex, suballocType, pAllocation);
    4241  // Allocation from this alternative memory type succeeded.
    4242  if(res == VK_SUCCESS)
    4243  {
    4244  return res;
    4245  }
    4246  // else: Allocation from this memory type failed. Try next one - next loop iteration.
    4247  }
    4248  // No other matching memory type index could be found.
    4249  else
    4250  // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
    4251  return VK_ERROR_OUT_OF_DEVICE_MEMORY;
    4252  }
    4253  }
    4254  }
    4255  // Can't find any single memory type maching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
    4256  else
    4257  return res;
    4258 }
    4259 
    4260 void VmaAllocator_T::FreeMemory(const VmaAllocation allocation)
    4261 {
    4262  VMA_ASSERT(allocation);
    4263 
    4264  if(allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK)
    4265  {
    4266  VmaBlock* pBlockToDelete = VMA_NULL;
    4267 
    4268  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
    4269  const VMA_BLOCK_VECTOR_TYPE blockVectorType = allocation->GetBlockVectorType();
    4270  {
    4271  VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4272 
    4273  VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType];
    4274  VmaBlock* pBlock = allocation->GetBlock();
    4275 
    4276  pBlock->Free(allocation);
    4277  VMA_HEAVY_ASSERT(pBlock->Validate());
    4278 
    4279  VMA_DEBUG_LOG(" Freed from MemoryTypeIndex=%u", memTypeIndex);
    4280 
    4281  // pBlock became empty after this deallocation.
    4282  if(pBlock->IsEmpty())
    4283  {
    4284  // Already has empty Allocation. We don't want to have two, so delete this one.
    4285  if(m_HasEmptyBlock[memTypeIndex])
    4286  {
    4287  pBlockToDelete = pBlock;
    4288  pBlockVector->Remove(pBlock);
    4289  }
    4290  // We now have first empty Allocation.
    4291  else
    4292  m_HasEmptyBlock[memTypeIndex] = true;
    4293  }
    4294  // Must be called after srcBlockIndex is used, because later it may become invalid!
    4295  pBlockVector->IncrementallySortBlocks();
    4296  }
    4297  // Destruction of a free Allocation. Deferred until this point, outside of mutex
    4298  // lock, for performance reason.
    4299  if(pBlockToDelete != VMA_NULL)
    4300  {
    4301  VMA_DEBUG_LOG(" Deleted empty allocation");
    4302  pBlockToDelete->Destroy(this);
    4303  vma_delete(this, pBlockToDelete);
    4304  }
    4305 
    4306  vma_delete(this, allocation);
    4307  }
    4308  else // VmaAllocation_T::ALLOCATION_TYPE_OWN
    4309  {
    4310  FreeOwnMemory(allocation);
    4311  }
    4312 }
    4313 
    4314 void VmaAllocator_T::CalculateStats(VmaStats* pStats)
    4315 {
    4316  InitStatInfo(pStats->total);
    4317  for(size_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i)
    4318  InitStatInfo(pStats->memoryType[i]);
    4319  for(size_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i)
    4320  InitStatInfo(pStats->memoryHeap[i]);
    4321 
    4322  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    4323  {
    4324  VmaMutexLock allocationsLock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4325  const uint32_t heapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex;
    4326  for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType)
    4327  {
    4328  const VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType];
    4329  VMA_ASSERT(pBlockVector);
    4330  pBlockVector->AddStats(pStats, memTypeIndex, heapIndex);
    4331  }
    4332  }
    4333 
    4334  VmaPostprocessCalcStatInfo(pStats->total);
    4335  for(size_t i = 0; i < GetMemoryTypeCount(); ++i)
    4336  VmaPostprocessCalcStatInfo(pStats->memoryType[i]);
    4337  for(size_t i = 0; i < GetMemoryHeapCount(); ++i)
    4338  VmaPostprocessCalcStatInfo(pStats->memoryHeap[i]);
    4339 }
    4340 
    4341 static const uint32_t VMA_VENDOR_ID_AMD = 4098;
    4342 
    4343 void VmaAllocator_T::UnmapPersistentlyMappedMemory()
    4344 {
    4345  if(m_UnmapPersistentlyMappedMemoryCounter++ == 0)
    4346  {
    4347  if(m_PhysicalDeviceProperties.vendorID == VMA_VENDOR_ID_AMD)
    4348  {
    4349  for(size_t memTypeIndex = m_MemProps.memoryTypeCount; memTypeIndex--; )
    4350  {
    4351  const VkMemoryPropertyFlags memFlags = m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
    4352  if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0 &&
    4353  (memFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
    4354  {
    4355  // Process OwnAllocations.
    4356  {
    4357  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex);
    4358  AllocationVectorType* pOwnAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED];
    4359  for(size_t ownAllocIndex = pOwnAllocationsVector->size(); ownAllocIndex--; )
    4360  {
    4361  VmaAllocation hAlloc = (*pOwnAllocationsVector)[ownAllocIndex];
    4362  hAlloc->OwnAllocUnmapPersistentlyMappedMemory(m_hDevice);
    4363  }
    4364  }
    4365 
    4366  // Process normal Allocations.
    4367  {
    4368  VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4369  VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED];
    4370  pBlockVector->UnmapPersistentlyMappedMemory();
    4371  }
    4372  }
    4373  }
    4374  }
    4375  }
    4376 }
    4377 
    4378 VkResult VmaAllocator_T::MapPersistentlyMappedMemory()
    4379 {
    4380  VMA_ASSERT(m_UnmapPersistentlyMappedMemoryCounter > 0);
    4381  if(--m_UnmapPersistentlyMappedMemoryCounter == 0)
    4382  {
    4383  VkResult finalResult = VK_SUCCESS;
    4384  if(m_PhysicalDeviceProperties.vendorID == VMA_VENDOR_ID_AMD)
    4385  {
    4386  for(size_t memTypeIndex = 0; memTypeIndex < m_MemProps.memoryTypeCount; ++memTypeIndex)
    4387  {
    4388  const VkMemoryPropertyFlags memFlags = m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
    4389  if((memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0 &&
    4390  (memFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
    4391  {
    4392  // Process OwnAllocations.
    4393  {
    4394  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex);
    4395  AllocationVectorType* pAllocationsVector = m_pOwnAllocations[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED];
    4396  for(size_t ownAllocIndex = 0, ownAllocCount = pAllocationsVector->size(); ownAllocIndex < ownAllocCount; ++ownAllocIndex)
    4397  {
    4398  VmaAllocation hAlloc = (*pAllocationsVector)[ownAllocIndex];
    4399  hAlloc->OwnAllocMapPersistentlyMappedMemory(m_hDevice);
    4400  }
    4401  }
    4402 
    4403  // Process normal Allocations.
    4404  {
    4405  VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4406  VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][VMA_BLOCK_VECTOR_TYPE_MAPPED];
    4407  VkResult localResult = pBlockVector->MapPersistentlyMappedMemory();
    4408  if(localResult != VK_SUCCESS)
    4409  {
    4410  finalResult = localResult;
    4411  }
    4412  }
    4413  }
    4414  }
    4415  }
    4416  return finalResult;
    4417  }
    4418  else
    4419  return VK_SUCCESS;
    4420 }
    4421 
    4422 VkResult VmaAllocator_T::Defragment(
    4423  VmaAllocation* pAllocations,
    4424  size_t allocationCount,
    4425  VkBool32* pAllocationsChanged,
    4426  const VmaDefragmentationInfo* pDefragmentationInfo,
    4427  VmaDefragmentationStats* pDefragmentationStats)
    4428 {
    4429  if(pAllocationsChanged != VMA_NULL)
    4430  {
    4431  memset(pAllocationsChanged, 0, sizeof(*pAllocationsChanged));
    4432  }
    4433  if(pDefragmentationStats != VMA_NULL)
    4434  {
    4435  memset(pDefragmentationStats, 0, sizeof(*pDefragmentationStats));
    4436  }
    4437 
    4438  if(m_UnmapPersistentlyMappedMemoryCounter > 0)
    4439  {
    4440  VMA_DEBUG_LOG("ERROR: Cannot defragment when inside vmaUnmapPersistentlyMappedMemory.");
    4441  return VK_ERROR_MEMORY_MAP_FAILED;
    4442  }
    4443 
    4444  // Initialize defragmentators per memory type.
    4445  const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity();
    4446  VmaDefragmentator* pDefragmentators[VK_MAX_MEMORY_TYPES][VMA_BLOCK_VECTOR_TYPE_COUNT];
    4447  memset(pDefragmentators, 0, sizeof(pDefragmentators));
    4448  for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    4449  {
    4450  // Only HOST_VISIBLE memory types can be defragmented.
    4451  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
    4452  {
    4453  for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType)
    4454  {
    4455  pDefragmentators[memTypeIndex][blockVectorType] = vma_new(this, VmaDefragmentator)(
    4456  m_hDevice,
    4457  GetAllocationCallbacks(),
    4458  bufferImageGranularity,
    4459  memTypeIndex,
    4460  (VMA_BLOCK_VECTOR_TYPE)blockVectorType);
    4461  }
    4462  }
    4463  }
    4464 
    4465  // Dispatch pAllocations among defragmentators.
    4466  for(size_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex)
    4467  {
    4468  VmaAllocation hAlloc = pAllocations[allocIndex];
    4469  VMA_ASSERT(hAlloc);
    4470  if(hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK)
    4471  {
    4472  const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
    4473  // Only HOST_VISIBLE memory types can be defragmented.
    4474  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
    4475  {
    4476  const VMA_BLOCK_VECTOR_TYPE blockVectorType = hAlloc->GetBlockVectorType();
    4477  VkBool32* pChanged = (pAllocationsChanged != VMA_NULL) ?
    4478  &pAllocationsChanged[allocIndex] : VMA_NULL;
    4479  pDefragmentators[memTypeIndex][blockVectorType]->AddAllocation(hAlloc, pChanged);
    4480  }
    4481  // else: skip this allocation, cannot move it.
    4482  }
    4483  // else ALLOCATION_TYPE_OWN: skip this allocation, nothing to defragment.
    4484  }
    4485 
    4486  VkResult result = VK_SUCCESS;
    4487 
    4488  // Main processing.
    4489  VkDeviceSize maxBytesToMove = SIZE_MAX;
    4490  uint32_t maxAllocationsToMove = UINT32_MAX;
    4491  if(pDefragmentationInfo != VMA_NULL)
    4492  {
    4493  maxBytesToMove = pDefragmentationInfo->maxBytesToMove;
    4494  maxAllocationsToMove = pDefragmentationInfo->maxAllocationsToMove;
    4495  }
    4496  for(uint32_t memTypeIndex = 0;
    4497  (memTypeIndex < GetMemoryTypeCount()) && (result == VK_SUCCESS);
    4498  ++memTypeIndex)
    4499  {
    4500  // Only HOST_VISIBLE memory types can be defragmented.
    4501  if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
    4502  {
    4503  VmaMutexLock lock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4504 
    4505  for(uint32_t blockVectorType = 0;
    4506  (blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT) && (result == VK_SUCCESS);
    4507  ++blockVectorType)
    4508  {
    4509  VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex][blockVectorType];
    4510 
    4511  // Defragment.
    4512  result = pDefragmentators[memTypeIndex][blockVectorType]->Defragment(pBlockVector, maxBytesToMove, maxAllocationsToMove);
    4513 
    4514  // Accumulate statistics.
    4515  if(pDefragmentationStats != VMA_NULL)
    4516  {
    4517  const VkDeviceSize bytesMoved = pDefragmentators[memTypeIndex][blockVectorType]->GetBytesMoved();
    4518  const uint32_t allocationsMoved = pDefragmentators[memTypeIndex][blockVectorType]->GetAllocationsMoved();
    4519  pDefragmentationStats->bytesMoved += bytesMoved;
    4520  pDefragmentationStats->allocationsMoved += allocationsMoved;
    4521  VMA_ASSERT(bytesMoved <= maxBytesToMove);
    4522  VMA_ASSERT(allocationsMoved <= maxAllocationsToMove);
    4523  maxBytesToMove -= bytesMoved;
    4524  maxAllocationsToMove -= allocationsMoved;
    4525  }
    4526 
    4527  // Free empty blocks.
    4528  for(size_t blockIndex = pBlockVector->m_Blocks.size(); blockIndex--; )
    4529  {
    4530  VmaBlock* pBlock = pBlockVector->m_Blocks[blockIndex];
    4531  if(pBlock->IsEmpty())
    4532  {
    4533  if(pDefragmentationStats != VMA_NULL)
    4534  {
    4535  ++pDefragmentationStats->deviceMemoryBlocksFreed;
    4536  pDefragmentationStats->bytesFreed += pBlock->m_Size;
    4537  }
    4538 
    4539  VectorRemove(pBlockVector->m_Blocks, blockIndex);
    4540  pBlock->Destroy(this);
    4541  vma_delete(this, pBlock);
    4542  }
    4543  }
    4544 
    4545  // All block vector types processed: we can be sure that all empty allocations have been freed.
    4546  if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_COUNT - 1)
    4547  {
    4548  m_HasEmptyBlock[memTypeIndex] = false;
    4549  }
    4550  }
    4551  }
    4552  }
    4553 
    4554  // Destroy defragmentators.
    4555  for(uint32_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; )
    4556  {
    4557  for(size_t blockVectorType = VMA_BLOCK_VECTOR_TYPE_COUNT; blockVectorType--; )
    4558  {
    4559  vma_delete(this, pDefragmentators[memTypeIndex][blockVectorType]);
    4560  }
    4561  }
    4562 
    4563  return result;
    4564 }
    4565 
    4566 void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo)
    4567 {
    4568  pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex();
    4569  pAllocationInfo->deviceMemory = hAllocation->GetMemory();
    4570  pAllocationInfo->offset = hAllocation->GetOffset();
    4571  pAllocationInfo->size = hAllocation->GetSize();
    4572  pAllocationInfo->pMappedData = hAllocation->GetMappedData();
    4573  pAllocationInfo->pUserData = hAllocation->GetUserData();
    4574 }
    4575 
    4576 void VmaAllocator_T::FreeOwnMemory(VmaAllocation allocation)
    4577 {
    4578  VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_OWN);
    4579 
    4580  const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
    4581  {
    4582  VmaMutexLock lock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex);
    4583  AllocationVectorType* const pOwnAllocations = m_pOwnAllocations[memTypeIndex][allocation->GetBlockVectorType()];
    4584  VMA_ASSERT(pOwnAllocations);
    4585  VmaAllocation* const pOwnAllocationsBeg = pOwnAllocations->data();
    4586  VmaAllocation* const pOwnAllocationsEnd = pOwnAllocationsBeg + pOwnAllocations->size();
    4587  VmaAllocation* const pOwnAllocationIt = VmaBinaryFindFirstNotLess(
    4588  pOwnAllocationsBeg,
    4589  pOwnAllocationsEnd,
    4590  allocation,
    4591  VmaPointerLess());
    4592  if(pOwnAllocationIt != pOwnAllocationsEnd)
    4593  {
    4594  const size_t ownAllocationIndex = pOwnAllocationIt - pOwnAllocationsBeg;
    4595  VectorRemove(*pOwnAllocations, ownAllocationIndex);
    4596  }
    4597  else
    4598  {
    4599  VMA_ASSERT(0);
    4600  }
    4601  }
    4602 
    4603  VkDeviceMemory hMemory = allocation->GetMemory();
    4604 
    4605  // Callback.
    4606  if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL)
    4607  {
    4608  (*m_DeviceMemoryCallbacks.pfnFree)(this, memTypeIndex, hMemory, allocation->GetSize());
    4609  }
    4610 
    4611  if(allocation->GetMappedData() != VMA_NULL)
    4612  {
    4613  vkUnmapMemory(m_hDevice, hMemory);
    4614  }
    4615 
    4616  vkFreeMemory(m_hDevice, hMemory, GetAllocationCallbacks());
    4617 
    4618  VMA_DEBUG_LOG(" Freed OwnMemory MemoryTypeIndex=%u", memTypeIndex);
    4619 
    4620  vma_delete(this, allocation);
    4621 }
    4622 
    4623 #if VMA_STATS_STRING_ENABLED
    4624 
    4625 void VmaAllocator_T::PrintDetailedMap(VmaStringBuilder& sb)
    4626 {
    4627  bool ownAllocationsStarted = false;
    4628  for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    4629  {
    4630  VmaMutexLock ownAllocationsLock(m_OwnAllocationsMutex[memTypeIndex], m_UseMutex);
    4631  for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType)
    4632  {
    4633  AllocationVectorType* const pOwnAllocVector = m_pOwnAllocations[memTypeIndex][blockVectorType];
    4634  VMA_ASSERT(pOwnAllocVector);
    4635  if(pOwnAllocVector->empty() == false)
    4636  {
    4637  if(ownAllocationsStarted)
    4638  sb.Add(",\n\t\"Type ");
    4639  else
    4640  {
    4641  sb.Add(",\n\"OwnAllocations\": {\n\t\"Type ");
    4642  ownAllocationsStarted = true;
    4643  }
    4644  sb.AddNumber(memTypeIndex);
    4645  if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED)
    4646  {
    4647  sb.Add(" Mapped");
    4648  }
    4649  sb.Add("\": [");
    4650 
    4651  for(size_t i = 0; i < pOwnAllocVector->size(); ++i)
    4652  {
    4653  const VmaAllocation hAlloc = (*pOwnAllocVector)[i];
    4654  if(i > 0)
    4655  sb.Add(",\n\t\t{ \"Size\": ");
    4656  else
    4657  sb.Add("\n\t\t{ \"Size\": ");
    4658  sb.AddNumber(hAlloc->GetSize());
    4659  sb.Add(", \"Type\": ");
    4660  sb.AddString(VMA_SUBALLOCATION_TYPE_NAMES[hAlloc->GetSuballocationType()]);
    4661  sb.Add(" }");
    4662  }
    4663 
    4664  sb.Add("\n\t]");
    4665  }
    4666  }
    4667  }
    4668  if(ownAllocationsStarted)
    4669  sb.Add("\n}");
    4670 
    4671  {
    4672  bool allocationsStarted = false;
    4673  for(size_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
    4674  {
    4675  VmaMutexLock globalAllocationsLock(m_BlocksMutex[memTypeIndex], m_UseMutex);
    4676  for(uint32_t blockVectorType = 0; blockVectorType < VMA_BLOCK_VECTOR_TYPE_COUNT; ++blockVectorType)
    4677  {
    4678  if(m_pBlockVectors[memTypeIndex][blockVectorType]->IsEmpty() == false)
    4679  {
    4680  if(allocationsStarted)
    4681  sb.Add(",\n\t\"Type ");
    4682  else
    4683  {
    4684  sb.Add(",\n\"Allocations\": {\n\t\"Type ");
    4685  allocationsStarted = true;
    4686  }
    4687  sb.AddNumber(memTypeIndex);
    4688  if(blockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED)
    4689  {
    4690  sb.Add(" Mapped");
    4691  }
    4692  sb.Add("\": [");
    4693 
    4694  m_pBlockVectors[memTypeIndex][blockVectorType]->PrintDetailedMap(sb);
    4695 
    4696  sb.Add("\n\t]");
    4697  }
    4698  }
    4699  }
    4700  if(allocationsStarted)
    4701  sb.Add("\n}");
    4702  }
    4703 }
    4704 
    4705 #endif // #if VMA_STATS_STRING_ENABLED
    4706 
    4707 static VkResult AllocateMemoryForImage(
    4708  VmaAllocator allocator,
    4709  VkImage image,
    4710  const VmaMemoryRequirements* pMemoryRequirements,
    4711  VmaSuballocationType suballocType,
    4712  VmaAllocation* pAllocation)
    4713 {
    4714  VMA_ASSERT(allocator && (image != VK_NULL_HANDLE) && pMemoryRequirements && pAllocation);
    4715 
    4716  VkMemoryRequirements vkMemReq = {};
    4717  vkGetImageMemoryRequirements(allocator->m_hDevice, image, &vkMemReq);
    4718 
    4719  return allocator->AllocateMemory(
    4720  vkMemReq,
    4721  *pMemoryRequirements,
    4722  suballocType,
    4723  pAllocation);
    4724 }
    4725 
    4727 // Public interface
    4728 
    4729 VkResult vmaCreateAllocator(
    4730  const VmaAllocatorCreateInfo* pCreateInfo,
    4731  VmaAllocator* pAllocator)
    4732 {
    4733  VMA_ASSERT(pCreateInfo && pAllocator);
    4734  VMA_DEBUG_LOG("vmaCreateAllocator");
    4735  *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo);
    4736  return VK_SUCCESS;
    4737 }
    4738 
    4739 void vmaDestroyAllocator(
    4740  VmaAllocator allocator)
    4741 {
    4742  if(allocator != VK_NULL_HANDLE)
    4743  {
    4744  VMA_DEBUG_LOG("vmaDestroyAllocator");
    4745  VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks;
    4746  vma_delete(&allocationCallbacks, allocator);
    4747  }
    4748 }
    4749 
    4751  VmaAllocator allocator,
    4752  const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
    4753 {
    4754  VMA_ASSERT(allocator && ppPhysicalDeviceProperties);
    4755  *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties;
    4756 }
    4757 
    4759  VmaAllocator allocator,
    4760  const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties)
    4761 {
    4762  VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties);
    4763  *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps;
    4764 }
    4765 
    4767  VmaAllocator allocator,
    4768  uint32_t memoryTypeIndex,
    4769  VkMemoryPropertyFlags* pFlags)
    4770 {
    4771  VMA_ASSERT(allocator && pFlags);
    4772  VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount());
    4773  *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags;
    4774 }
    4775 
    4776 void vmaCalculateStats(
    4777  VmaAllocator allocator,
    4778  VmaStats* pStats)
    4779 {
    4780  VMA_ASSERT(allocator && pStats);
    4781  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    4782  allocator->CalculateStats(pStats);
    4783 }
    4784 
    4785 #if VMA_STATS_STRING_ENABLED
    4786 
    4787 void vmaBuildStatsString(
    4788  VmaAllocator allocator,
    4789  char** ppStatsString,
    4790  VkBool32 detailedMap)
    4791 {
    4792  VMA_ASSERT(allocator && ppStatsString);
    4793  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    4794 
    4795  VmaStringBuilder sb(allocator);
    4796  {
    4797  VmaStats stats;
    4798  allocator->CalculateStats(&stats);
    4799 
    4800  sb.Add("{\n\"Total\": ");
    4801  VmaPrintStatInfo(sb, stats.total);
    4802 
    4803  for(uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex)
    4804  {
    4805  sb.Add(",\n\"Heap ");
    4806  sb.AddNumber(heapIndex);
    4807  sb.Add("\": {\n\t\"Size\": ");
    4808  sb.AddNumber(allocator->m_MemProps.memoryHeaps[heapIndex].size);
    4809  sb.Add(",\n\t\"Flags\": ");
    4810  if((allocator->m_MemProps.memoryHeaps[heapIndex].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) != 0)
    4811  sb.AddString("DEVICE_LOCAL");
    4812  else
    4813  sb.AddString("");
    4814  if(stats.memoryHeap[heapIndex].AllocationCount > 0)
    4815  {
    4816  sb.Add(",\n\t\"Stats:\": ");
    4817  VmaPrintStatInfo(sb, stats.memoryHeap[heapIndex]);
    4818  }
    4819 
    4820  for(uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex)
    4821  {
    4822  if(allocator->m_MemProps.memoryTypes[typeIndex].heapIndex == heapIndex)
    4823  {
    4824  sb.Add(",\n\t\"Type ");
    4825  sb.AddNumber(typeIndex);
    4826  sb.Add("\": {\n\t\t\"Flags\": \"");
    4827  VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags;
    4828  if((flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != 0)
    4829  sb.Add(" DEVICE_LOCAL");
    4830  if((flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0)
    4831  sb.Add(" HOST_VISIBLE");
    4832  if((flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) != 0)
    4833  sb.Add(" HOST_COHERENT");
    4834  if((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) != 0)
    4835  sb.Add(" HOST_CACHED");
    4836  if((flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) != 0)
    4837  sb.Add(" LAZILY_ALLOCATED");
    4838  sb.Add("\"");
    4839  if(stats.memoryType[typeIndex].AllocationCount > 0)
    4840  {
    4841  sb.Add(",\n\t\t\"Stats\": ");
    4842  VmaPrintStatInfo(sb, stats.memoryType[typeIndex]);
    4843  }
    4844  sb.Add("\n\t}");
    4845  }
    4846  }
    4847  sb.Add("\n}");
    4848  }
    4849  if(detailedMap == VK_TRUE)
    4850  allocator->PrintDetailedMap(sb);
    4851  sb.Add("\n}\n");
    4852  }
    4853 
    4854  const size_t len = sb.GetLength();
    4855  char* const pChars = vma_new_array(allocator, char, len + 1);
    4856  if(len > 0)
    4857  memcpy(pChars, sb.GetData(), len);
    4858  pChars[len] = '\0';
    4859  *ppStatsString = pChars;
    4860 }
    4861 
    4862 void vmaFreeStatsString(
    4863  VmaAllocator allocator,
    4864  char* pStatsString)
    4865 {
    4866  if(pStatsString != VMA_NULL)
    4867  {
    4868  VMA_ASSERT(allocator);
    4869  size_t len = strlen(pStatsString);
    4870  vma_delete_array(allocator, pStatsString, len + 1);
    4871  }
    4872 }
    4873 
    4874 #endif // #if VMA_STATS_STRING_ENABLED
    4875 
    4878 VkResult vmaFindMemoryTypeIndex(
    4879  VmaAllocator allocator,
    4880  uint32_t memoryTypeBits,
    4881  const VmaMemoryRequirements* pMemoryRequirements,
    4882  uint32_t* pMemoryTypeIndex)
    4883 {
    4884  VMA_ASSERT(allocator != VK_NULL_HANDLE);
    4885  VMA_ASSERT(pMemoryRequirements != VMA_NULL);
    4886  VMA_ASSERT(pMemoryTypeIndex != VMA_NULL);
    4887 
    4888  uint32_t requiredFlags = pMemoryRequirements->requiredFlags;
    4889  uint32_t preferredFlags = pMemoryRequirements->preferredFlags;
    4890  if(preferredFlags == 0)
    4891  preferredFlags = requiredFlags;
    4892  // preferredFlags, if not 0, must be a superset of requiredFlags.
    4893  VMA_ASSERT((requiredFlags & ~preferredFlags) == 0);
    4894 
    4895  // Convert usage to requiredFlags and preferredFlags.
    4896  switch(pMemoryRequirements->usage)
    4897  {
    4899  break;
    4901  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    4902  break;
    4904  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
    4905  break;
    4907  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    4908  preferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    4909  break;
    4911  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    4912  preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
    4913  break;
    4914  default:
    4915  break;
    4916  }
    4917 
    4918  if((pMemoryRequirements->flags & VMA_MEMORY_REQUIREMENT_PERSISTENT_MAP_BIT) != 0)
    4919  requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
    4920 
    4921  *pMemoryTypeIndex = UINT32_MAX;
    4922  uint32_t minCost = UINT32_MAX;
    4923  for(uint32_t memTypeIndex = 0, memTypeBit = 1;
    4924  memTypeIndex < allocator->GetMemoryTypeCount();
    4925  ++memTypeIndex, memTypeBit <<= 1)
    4926  {
    4927  // This memory type is acceptable according to memoryTypeBits bitmask.
    4928  if((memTypeBit & memoryTypeBits) != 0)
    4929  {
    4930  const VkMemoryPropertyFlags currFlags =
    4931  allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags;
    4932  // This memory type contains requiredFlags.
    4933  if((requiredFlags & ~currFlags) == 0)
    4934  {
    4935  // Calculate cost as number of bits from preferredFlags not present in this memory type.
    4936  uint32_t currCost = CountBitsSet(preferredFlags & ~currFlags);
    4937  // Remember memory type with lowest cost.
    4938  if(currCost < minCost)
    4939  {
    4940  *pMemoryTypeIndex = memTypeIndex;
    4941  if(currCost == 0)
    4942  return VK_SUCCESS;
    4943  minCost = currCost;
    4944  }
    4945  }
    4946  }
    4947  }
    4948  return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT;
    4949 }
    4950 
    4951 VkResult vmaAllocateMemory(
    4952  VmaAllocator allocator,
    4953  const VkMemoryRequirements* pVkMemoryRequirements,
    4954  const VmaMemoryRequirements* pVmaMemoryRequirements,
    4955  VmaAllocation* pAllocation,
    4956  VmaAllocationInfo* pAllocationInfo)
    4957 {
    4958  VMA_ASSERT(allocator && pVkMemoryRequirements && pVmaMemoryRequirements && pAllocation);
    4959 
    4960  VMA_DEBUG_LOG("vmaAllocateMemory");
    4961 
    4962  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    4963 
    4964  return allocator->AllocateMemory(
    4965  *pVkMemoryRequirements,
    4966  *pVmaMemoryRequirements,
    4967  VMA_SUBALLOCATION_TYPE_UNKNOWN,
    4968  pAllocation);
    4969 
    4970  if(pAllocationInfo)
    4971  {
    4972  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
    4973  }
    4974 }
    4975 
    4977  VmaAllocator allocator,
    4978  VkBuffer buffer,
    4979  const VmaMemoryRequirements* pMemoryRequirements,
    4980  VmaAllocation* pAllocation,
    4981  VmaAllocationInfo* pAllocationInfo)
    4982 {
    4983  VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pMemoryRequirements && pAllocation);
    4984 
    4985  VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer");
    4986 
    4987  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    4988 
    4989  VkMemoryRequirements vkMemReq = {};
    4990  vkGetBufferMemoryRequirements(allocator->m_hDevice, buffer, &vkMemReq);
    4991 
    4992  return allocator->AllocateMemory(
    4993  vkMemReq,
    4994  *pMemoryRequirements,
    4995  VMA_SUBALLOCATION_TYPE_BUFFER,
    4996  pAllocation);
    4997 
    4998  if(pAllocationInfo)
    4999  {
    5000  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
    5001  }
    5002 }
    5003 
    5004 VkResult vmaAllocateMemoryForImage(
    5005  VmaAllocator allocator,
    5006  VkImage image,
    5007  const VmaMemoryRequirements* pMemoryRequirements,
    5008  VmaAllocation* pAllocation,
    5009  VmaAllocationInfo* pAllocationInfo)
    5010 {
    5011  VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pMemoryRequirements && pAllocation);
    5012 
    5013  VMA_DEBUG_LOG("vmaAllocateMemoryForImage");
    5014 
    5015  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5016 
    5017  return AllocateMemoryForImage(
    5018  allocator,
    5019  image,
    5020  pMemoryRequirements,
    5021  VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN,
    5022  pAllocation);
    5023 
    5024  if(pAllocationInfo)
    5025  {
    5026  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
    5027  }
    5028 }
    5029 
    5030 void vmaFreeMemory(
    5031  VmaAllocator allocator,
    5032  VmaAllocation allocation)
    5033 {
    5034  VMA_ASSERT(allocator && allocation);
    5035 
    5036  VMA_DEBUG_LOG("vmaFreeMemory");
    5037 
    5038  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5039 
    5040  allocator->FreeMemory(allocation);
    5041 }
    5042 
    5044  VmaAllocator allocator,
    5045  VmaAllocation allocation,
    5046  VmaAllocationInfo* pAllocationInfo)
    5047 {
    5048  VMA_ASSERT(allocator && allocation && pAllocationInfo);
    5049 
    5050  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5051 
    5052  allocator->GetAllocationInfo(allocation, pAllocationInfo);
    5053 }
    5054 
    5056  VmaAllocator allocator,
    5057  VmaAllocation allocation,
    5058  void* pUserData)
    5059 {
    5060  VMA_ASSERT(allocator && allocation);
    5061 
    5062  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5063 
    5064  allocation->SetUserData(pUserData);
    5065 }
    5066 
    5067 VkResult vmaMapMemory(
    5068  VmaAllocator allocator,
    5069  VmaAllocation allocation,
    5070  void** ppData)
    5071 {
    5072  VMA_ASSERT(allocator && allocation && ppData);
    5073 
    5074  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5075 
    5076  return vkMapMemory(allocator->m_hDevice, allocation->GetMemory(),
    5077  allocation->GetOffset(), allocation->GetSize(), 0, ppData);
    5078 }
    5079 
    5080 void vmaUnmapMemory(
    5081  VmaAllocator allocator,
    5082  VmaAllocation allocation)
    5083 {
    5084  VMA_ASSERT(allocator && allocation);
    5085 
    5086  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5087 
    5088  vkUnmapMemory(allocator->m_hDevice, allocation->GetMemory());
    5089 }
    5090 
    5091 void vmaUnmapPersistentlyMappedMemory(VmaAllocator allocator)
    5092 {
    5093  VMA_ASSERT(allocator);
    5094 
    5095  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5096 
    5097  allocator->UnmapPersistentlyMappedMemory();
    5098 }
    5099 
    5100 VkResult vmaMapPersistentlyMappedMemory(VmaAllocator allocator)
    5101 {
    5102  VMA_ASSERT(allocator);
    5103 
    5104  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5105 
    5106  return allocator->MapPersistentlyMappedMemory();
    5107 }
    5108 
    5109 VkResult vmaDefragment(
    5110  VmaAllocator allocator,
    5111  VmaAllocation* pAllocations,
    5112  size_t allocationCount,
    5113  VkBool32* pAllocationsChanged,
    5114  const VmaDefragmentationInfo *pDefragmentationInfo,
    5115  VmaDefragmentationStats* pDefragmentationStats)
    5116 {
    5117  VMA_ASSERT(allocator && pAllocations);
    5118 
    5119  VMA_DEBUG_LOG("vmaDefragment");
    5120 
    5121  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5122 
    5123  return allocator->Defragment(pAllocations, allocationCount, pAllocationsChanged, pDefragmentationInfo, pDefragmentationStats);
    5124 }
    5125 
    5126 VkResult vmaCreateBuffer(
    5127  VmaAllocator allocator,
    5128  const VkBufferCreateInfo* pCreateInfo,
    5129  const VmaMemoryRequirements* pMemoryRequirements,
    5130  VkBuffer* pBuffer,
    5131  VmaAllocation* pAllocation,
    5132  VmaAllocationInfo* pAllocationInfo)
    5133 {
    5134  VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements && pBuffer && pAllocation);
    5135 
    5136  VMA_DEBUG_LOG("vmaCreateBuffer");
    5137 
    5138  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5139 
    5140  // 1. Create VkBuffer.
    5141  VkResult res = vkCreateBuffer(allocator->m_hDevice, pCreateInfo, allocator->GetAllocationCallbacks(), pBuffer);
    5142  if(res >= 0)
    5143  {
    5144  // 2. vkGetBufferMemoryRequirements.
    5145  VkMemoryRequirements vkMemReq = {};
    5146  vkGetBufferMemoryRequirements(allocator->m_hDevice, *pBuffer, &vkMemReq);
    5147 
    5148  // 3. Allocate memory using allocator.
    5149  res = allocator->AllocateMemory(
    5150  vkMemReq,
    5151  *pMemoryRequirements,
    5152  VMA_SUBALLOCATION_TYPE_BUFFER,
    5153  pAllocation);
    5154  if(res >= 0)
    5155  {
    5156  // 3. Bind buffer with memory.
    5157  res = vkBindBufferMemory(allocator->m_hDevice, *pBuffer, (*pAllocation)->GetMemory(), (*pAllocation)->GetOffset());
    5158  if(res >= 0)
    5159  {
    5160  // All steps succeeded.
    5161  if(pAllocationInfo != VMA_NULL)
    5162  {
    5163  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
    5164  }
    5165  return VK_SUCCESS;
    5166  }
    5167  allocator->FreeMemory(*pAllocation);
    5168  return res;
    5169  }
    5170  vkDestroyBuffer(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks());
    5171  return res;
    5172  }
    5173  return res;
    5174 }
    5175 
    5176 void vmaDestroyBuffer(
    5177  VmaAllocator allocator,
    5178  VkBuffer buffer,
    5179  VmaAllocation allocation)
    5180 {
    5181  if(buffer != VK_NULL_HANDLE)
    5182  {
    5183  VMA_ASSERT(allocator);
    5184 
    5185  VMA_DEBUG_LOG("vmaDestroyBuffer");
    5186 
    5187  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5188 
    5189  vkDestroyBuffer(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks());
    5190 
    5191  allocator->FreeMemory(allocation);
    5192  }
    5193 }
    5194 
    5195 VkResult vmaCreateImage(
    5196  VmaAllocator allocator,
    5197  const VkImageCreateInfo* pCreateInfo,
    5198  const VmaMemoryRequirements* pMemoryRequirements,
    5199  VkImage* pImage,
    5200  VmaAllocation* pAllocation,
    5201  VmaAllocationInfo* pAllocationInfo)
    5202 {
    5203  VMA_ASSERT(allocator && pCreateInfo && pMemoryRequirements && pImage && pAllocation);
    5204 
    5205  VMA_DEBUG_LOG("vmaCreateImage");
    5206 
    5207  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5208 
    5209  // 1. Create VkImage.
    5210  VkResult res = vkCreateImage(allocator->m_hDevice, pCreateInfo, allocator->GetAllocationCallbacks(), pImage);
    5211  if(res >= 0)
    5212  {
    5213  VkMappedMemoryRange mem = {};
    5214  VmaSuballocationType suballocType = pCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ?
    5215  VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL :
    5216  VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR;
    5217 
    5218  // 2. Allocate memory using allocator.
    5219  res = AllocateMemoryForImage(allocator, *pImage, pMemoryRequirements, suballocType, pAllocation);
    5220  if(res >= 0)
    5221  {
    5222  // 3. Bind image with memory.
    5223  res = vkBindImageMemory(allocator->m_hDevice, *pImage, (*pAllocation)->GetMemory(), (*pAllocation)->GetOffset());
    5224  if(res >= 0)
    5225  {
    5226  // All steps succeeded.
    5227  if(pAllocationInfo != VMA_NULL)
    5228  {
    5229  allocator->GetAllocationInfo(*pAllocation, pAllocationInfo);
    5230  }
    5231  return VK_SUCCESS;
    5232  }
    5233  allocator->FreeMemory(*pAllocation);
    5234  return res;
    5235  }
    5236  vkDestroyImage(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks());
    5237  return res;
    5238  }
    5239  return res;
    5240 }
    5241 
    5242 void vmaDestroyImage(
    5243  VmaAllocator allocator,
    5244  VkImage image,
    5245  VmaAllocation allocation)
    5246 {
    5247  if(image != VK_NULL_HANDLE)
    5248  {
    5249  VMA_ASSERT(allocator);
    5250 
    5251  VMA_DEBUG_LOG("vmaDestroyImage");
    5252 
    5253  VMA_DEBUG_GLOBAL_MUTEX_LOCK
    5254 
    5255  vkDestroyImage(allocator->m_hDevice, image, allocator->GetAllocationCallbacks());
    5256 
    5257  allocator->FreeMemory(allocation);
    5258  }
    5259 }
    5260 
    5261 #endif // #ifdef VMA_IMPLEMENTATION
    VmaMemoryRequirementFlagBits
    Flags to be passed as VmaMemoryRequirements::flags.
    Definition: vk_mem_alloc.h:336
    +
    Set this flag if the allocation should have its own memory block.
    Definition: vk_mem_alloc.h:345
    +
    struct VmaMemoryRequirements VmaMemoryRequirements
    +
    VkPhysicalDevice physicalDevice
    Vulkan physical device.
    Definition: vk_mem_alloc.h:214
    +
    VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
    +
    Memory will be used for writing on device and readback on host.
    Definition: vk_mem_alloc.h:331
    +
    VmaMemoryUsage usage
    Intended usage of memory.
    Definition: vk_mem_alloc.h:374
    +
    VkDeviceMemory deviceMemory
    Handle to Vulkan memory object.
    Definition: vk_mem_alloc.h:431
    +
    uint32_t maxAllocationsToMove
    Maximum number of allocations that can be moved to different place.
    Definition: vk_mem_alloc.h:561
    +
    void vmaGetAllocationInfo(VmaAllocator allocator, VmaAllocation allocation, VmaAllocationInfo *pAllocationInfo)
    Returns current information about specified allocation.
    +
    void vmaUnmapPersistentlyMappedMemory(VmaAllocator allocator)
    Unmaps persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL.
    +
    void vmaDestroyImage(VmaAllocator allocator, VkImage image, VmaAllocation allocation)
    +
    struct VmaDefragmentationInfo VmaDefragmentationInfo
    Optional configuration parameters to be passed to function vmaDefragment().
    +
    void(VKAPI_PTR * PFN_vmaFreeDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
    Callback function called before vkFreeMemory.
    Definition: vk_mem_alloc.h:177
    +
    const VkAllocationCallbacks * pAllocationCallbacks
    Custom allocation callbacks.
    Definition: vk_mem_alloc.h:226
    +
    VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
    +
    Description of a Allocator to be created.
    Definition: vk_mem_alloc.h:208
    +
    VkDeviceSize preferredSmallHeapBlockSize
    Size of a single memory block to allocate for resources from a small heap <= 512 MB.
    Definition: vk_mem_alloc.h:223
    +
    VmaMemoryRequirementFlags flags
    Definition: vk_mem_alloc.h:369
    +
    VkFlags VmaAllocatorFlags
    Definition: vk_mem_alloc.h:205
    +
    Statistics returned by function vmaDefragment().
    Definition: vk_mem_alloc.h:565
    +
    VmaStatInfo total
    Definition: vk_mem_alloc.h:284
    +
    uint32_t deviceMemoryBlocksFreed
    Number of empty VkDeviceMemory objects that have been released to the system.
    Definition: vk_mem_alloc.h:573
    +
    VkDeviceSize maxBytesToMove
    Maximum total numbers of bytes that can be copied while moving allocations to different places...
    Definition: vk_mem_alloc.h:556
    +
    VkDevice device
    Vulkan device.
    Definition: vk_mem_alloc.h:217
    +
    VkDeviceSize size
    Size of this allocation, in bytes.
    Definition: vk_mem_alloc.h:441
    +
    void vmaFreeMemory(VmaAllocator allocator, VmaAllocation allocation)
    Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage().
    +
    Set this flag to only try to allocate from existing VkDeviceMemory blocks and never create new such b...
    Definition: vk_mem_alloc.h:354
    void vmaBuildStatsString(VmaAllocator allocator, char **ppStatsString, VkBool32 detailedMap)
    Builds and returns statistics as string in JSON format.
    -
    void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer)
    -
    VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
    Function similar to vmaCreateBuffer().
    -
    General statistics from current state of Allocator.
    Definition: vk_mem_alloc.h:232
    +
    Optional configuration parameters to be passed to function vmaDefragment().
    Definition: vk_mem_alloc.h:551
    +
    VkDeviceSize bytesFreed
    Total number of bytes that have been released to the system by freeing empty VkDeviceMemory objects...
    Definition: vk_mem_alloc.h:569
    +
    Definition: vk_mem_alloc.h:363
    +
    void * pUserData
    Custom general-purpose pointer that will be stored in VmaAllocation, can be read as VmaAllocationInfo...
    Definition: vk_mem_alloc.h:385
    +
    General statistics from current state of Allocator.
    Definition: vk_mem_alloc.h:280
    VkResult vmaCreateAllocator(const VmaAllocatorCreateInfo *pCreateInfo, VmaAllocator *pAllocator)
    Creates Allocator object.
    -
    VkMemoryPropertyFlags preferredFlags
    Flags that preferably should be set in a Memory Type chosen for an allocation.
    Definition: vk_mem_alloc.h:308
    -
    VmaMemoryUsage
    Definition: vk_mem_alloc.h:269
    +
    VkMemoryPropertyFlags preferredFlags
    Flags that preferably should be set in a Memory Type chosen for an allocation.
    Definition: vk_mem_alloc.h:383
    +
    uint32_t allocationsMoved
    Number of allocations that have been moved to different places.
    Definition: vk_mem_alloc.h:571
    +
    VmaMemoryUsage
    Definition: vk_mem_alloc.h:317
    void vmaDestroyAllocator(VmaAllocator allocator)
    Destroys allocator object.
    -
    VkResult vmaCreateBuffer(VmaAllocator allocator, const VkBufferCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkBuffer *pBuffer, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
    +
    Allocator and all objects created from it will not be synchronized internally, so you must guarantee ...
    Definition: vk_mem_alloc.h:201
    void vmaCalculateStats(VmaAllocator allocator, VmaStats *pStats)
    Retrieves statistics from current state of the Allocator.
    -
    Definition: vk_mem_alloc.h:220
    -
    VkMemoryPropertyFlags requiredFlags
    Flags that must be set in a Memory Type chosen for an allocation.
    Definition: vk_mem_alloc.h:303
    -
    Definition: vk_mem_alloc.h:284
    -
    VkBool32 neverAllocate
    Set this flag to only try to allocate from existing VkDeviceMemory blocks and never create new such b...
    Definition: vk_mem_alloc.h:315
    -
    VkDeviceSize UnusedRangeSizeMax
    Definition: vk_mem_alloc.h:228
    -
    VkDeviceSize SuballocationSizeMax
    Definition: vk_mem_alloc.h:227
    +
    VmaAllocatorFlagBits
    Flags for created VmaAllocator.
    Definition: vk_mem_alloc.h:196
    +
    void vmaSetAllocationUserData(VmaAllocator allocator, VmaAllocation allocation, void *pUserData)
    Sets pUserData in given allocation to new value.
    +
    Definition: vk_mem_alloc.h:268
    +
    Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
    Definition: vk_mem_alloc.h:188
    +
    VkMemoryPropertyFlags requiredFlags
    Flags that must be set in a Memory Type chosen for an allocation.
    Definition: vk_mem_alloc.h:378
    +
    Definition: vk_mem_alloc.h:367
    +
    PFN_vmaFreeDeviceMemoryFunction pfnFree
    Optional, can be null.
    Definition: vk_mem_alloc.h:192
    +
    VkResult vmaMapPersistentlyMappedMemory(VmaAllocator allocator)
    Maps back persistently mapped memory of types that is HOST_COHERENT and DEVICE_LOCAL.
    +
    VkFlags VmaMemoryRequirementFlags
    Definition: vk_mem_alloc.h:365
    +
    VkDeviceSize UnusedRangeSizeMax
    Definition: vk_mem_alloc.h:276
    +
    VkDeviceSize SuballocationSizeMax
    Definition: vk_mem_alloc.h:275
    struct VmaAllocatorCreateInfo VmaAllocatorCreateInfo
    Description of a Allocator to be created.
    -
    VkBool32 ownMemory
    Set to true if this allocation should have its own memory block.
    Definition: vk_mem_alloc.h:294
    -
    VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
    Definition: vk_mem_alloc.h:234
    -
    void vmaDestroyImage(VmaAllocator allocator, VkImage image)
    -
    uint32_t AllocationCount
    Definition: vk_mem_alloc.h:222
    +
    void(VKAPI_PTR * PFN_vmaAllocateDeviceMemoryFunction)(VmaAllocator allocator, uint32_t memoryType, VkDeviceMemory memory, VkDeviceSize size)
    Callback function called after successful vkAllocateMemory.
    Definition: vk_mem_alloc.h:171
    +
    VkResult vmaAllocateMemory(VmaAllocator allocator, const VkMemoryRequirements *pVkMemoryRequirements, const VmaMemoryRequirements *pVmaMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
    General purpose memory allocation.
    +
    const VmaDeviceMemoryCallbacks * pDeviceMemoryCallbacks
    Informative callbacks for vkAllocateMemory, vkFreeMemory.
    Definition: vk_mem_alloc.h:229
    +
    VmaStatInfo memoryType[VK_MAX_MEMORY_TYPES]
    Definition: vk_mem_alloc.h:282
    +
    uint32_t AllocationCount
    Definition: vk_mem_alloc.h:270
    +
    VkResult vmaMapMemory(VmaAllocator allocator, VmaAllocation allocation, void **ppData)
    +
    PFN_vmaAllocateDeviceMemoryFunction pfnAllocate
    Optional, can be null.
    Definition: vk_mem_alloc.h:190
    +
    VmaAllocatorFlags flags
    Flags for created allocator. Use VmaAllocatorFlagBits enum.
    Definition: vk_mem_alloc.h:211
    void vmaGetPhysicalDeviceProperties(VmaAllocator allocator, const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties)
    -
    VkDeviceSize UsedBytes
    Definition: vk_mem_alloc.h:225
    -
    VkDeviceSize preferredLargeHeapBlockSize
    Size of a single memory block to allocate for resources.
    Definition: vk_mem_alloc.h:175
    -
    uint32_t UnusedRangeCount
    Definition: vk_mem_alloc.h:224
    -
    Memory will be mapped on host. Could be used for transfer to device.
    Definition: vk_mem_alloc.h:276
    +
    VkDeviceSize UsedBytes
    Definition: vk_mem_alloc.h:273
    +
    void * pUserData
    Custom general-purpose pointer that was passed as VmaMemoryRequirements::pUserData or set using vmaSe...
    Definition: vk_mem_alloc.h:452
    +
    VkDeviceSize preferredLargeHeapBlockSize
    Size of a single memory block to allocate for resources.
    Definition: vk_mem_alloc.h:220
    +
    VkResult vmaAllocateMemoryForImage(VmaAllocator allocator, VkImage image, const VmaMemoryRequirements *pMemoryRequirements, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
    Function similar to vmaAllocateMemoryForBuffer().
    +
    uint32_t UnusedRangeCount
    Definition: vk_mem_alloc.h:272
    +
    Memory will be mapped on host. Could be used for transfer to device.
    Definition: vk_mem_alloc.h:325
    void vmaGetMemoryProperties(VmaAllocator allocator, const VkPhysicalDeviceMemoryProperties **ppPhysicalDeviceMemoryProperties)
    -
    uint32_t SuballocationCount
    Definition: vk_mem_alloc.h:223
    -
    VkDeviceSize UnusedRangeSizeAvg
    Definition: vk_mem_alloc.h:228
    -
    VkDeviceSize SuballocationSizeMin
    Definition: vk_mem_alloc.h:227
    -
    VkResult vmaAllocateMemoryForBuffer(VmaAllocator allocator, VkBuffer buffer, const VmaMemoryRequirements *pMemoryRequirements, VkMappedMemoryRange *pMemory, uint32_t *pMemoryTypeIndex)
    -
    VkDeviceSize SuballocationSizeAvg
    Definition: vk_mem_alloc.h:227
    +
    uint32_t SuballocationCount
    Definition: vk_mem_alloc.h:271
    +
    VkDeviceSize UnusedRangeSizeAvg
    Definition: vk_mem_alloc.h:276
    +
    VkDeviceSize offset
    Offset into deviceMemory object to the beginning of this allocation, in bytes. (deviceMemory, offset) pair is unique to this allocation.
    Definition: vk_mem_alloc.h:436
    +
    VkDeviceSize bytesMoved
    Total number of bytes that have been copied while moving allocations to different places...
    Definition: vk_mem_alloc.h:567
    +
    VkResult vmaDefragment(VmaAllocator allocator, VmaAllocation *pAllocations, size_t allocationCount, VkBool32 *pAllocationsChanged, const VmaDefragmentationInfo *pDefragmentationInfo, VmaDefragmentationStats *pDefragmentationStats)
    Compacts memory by moving allocations.
    +
    struct VmaDeviceMemoryCallbacks VmaDeviceMemoryCallbacks
    Set of callbacks that the library will call for vkAllocateMemory and vkFreeMemory.
    +
    VkDeviceSize SuballocationSizeMin
    Definition: vk_mem_alloc.h:275
    +
    VkResult vmaCreateImage(VmaAllocator allocator, const VkImageCreateInfo *pCreateInfo, const VmaMemoryRequirements *pMemoryRequirements, VkImage *pImage, VmaAllocation *pAllocation, VmaAllocationInfo *pAllocationInfo)
    Function similar to vmaCreateBuffer().
    +
    void * pMappedData
    Pointer to the beginning of this allocation as mapped data. Null if this alloaction is not persistent...
    Definition: vk_mem_alloc.h:447
    +
    VkDeviceSize SuballocationSizeAvg
    Definition: vk_mem_alloc.h:275
    void vmaFreeStatsString(VmaAllocator allocator, char *pStatsString)
    -
    No intended memory usage specified.
    Definition: vk_mem_alloc.h:272
    -
    Definition: vk_mem_alloc.h:281
    -
    Memory will be used for frequent (dynamic) updates from host and reads on device. ...
    Definition: vk_mem_alloc.h:278
    +
    Set to use a memory that will be persistently mapped and retrieve pointer to it.
    Definition: vk_mem_alloc.h:361
    +
    No intended memory usage specified.
    Definition: vk_mem_alloc.h:320
    +
    Definition: vk_mem_alloc.h:332
    +
    Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
    Definition: vk_mem_alloc.h:419
    +
    Memory will be used for frequent (dynamic) updates from host and reads on device. ...
    Definition: vk_mem_alloc.h:328
    +
    Definition: vk_mem_alloc.h:203
    +
    struct VmaAllocationInfo VmaAllocationInfo
    Parameters of VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo().
    void vmaGetMemoryTypeProperties(VmaAllocator allocator, uint32_t memoryTypeIndex, VkMemoryPropertyFlags *pFlags)
    Given Memory Type Index, returns Property Flags of this memory type.
    -
    Memory will be used on device only, no need to be mapped on host.
    Definition: vk_mem_alloc.h:274
    +
    Memory will be used on device only, no need to be mapped on host.
    Definition: vk_mem_alloc.h:322
    struct VmaStatInfo VmaStatInfo
    -
    VkDeviceSize UnusedBytes
    Definition: vk_mem_alloc.h:226
    -
    VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
    Definition: vk_mem_alloc.h:235
    +
    VkDeviceSize UnusedBytes
    Definition: vk_mem_alloc.h:274
    +
    void vmaUnmapMemory(VmaAllocator allocator, VmaAllocation allocation)
    +
    VmaStatInfo memoryHeap[VK_MAX_MEMORY_HEAPS]
    Definition: vk_mem_alloc.h:283
    VkResult vmaFindMemoryTypeIndex(VmaAllocator allocator, uint32_t memoryTypeBits, const VmaMemoryRequirements *pMemoryRequirements, uint32_t *pMemoryTypeIndex)
    -
    void vmaFreeMemory(VmaAllocator allocator, const VkMappedMemoryRange *pMemory)
    Frees memory previously allocated using vmaAllocateMemoryForBuffer() or vmaAllocateMemoryForImage().
    -
    VkDeviceSize UnusedRangeSizeMin
    Definition: vk_mem_alloc.h:228
    +
    struct VmaDefragmentationStats VmaDefragmentationStats
    Statistics returned by function vmaDefragment().
    +
    void vmaDestroyBuffer(VmaAllocator allocator, VkBuffer buffer, VmaAllocation allocation)
    +
    VkDeviceSize UnusedRangeSizeMin
    Definition: vk_mem_alloc.h:276
    +
    uint32_t memoryType
    Memory type index that this allocation was allocated from.
    Definition: vk_mem_alloc.h:424