Pārlūkot izejas kodu

Add a separate pool for small allocations in Vulkan RD

Pedro J. Estébanez 3 gadi atpakaļ
vecāks
revīzija
4e6c9d3ae9

+ 20 - 2
drivers/vulkan/rendering_device_vulkan.cpp

@@ -43,6 +43,8 @@
 
 //#define FORCE_FULL_BARRIER
 
+static const uint32_t SMALL_ALLOCATION_MAX_SIZE = 4096;
+
 // Get the Vulkan object information and possible stage access types (bitwise OR'd with incoming values)
 RenderingDeviceVulkan::Buffer *RenderingDeviceVulkan::_get_buffer_from_owner(RID p_buffer, VkPipelineStageFlags &r_stage_mask, VkAccessFlags &r_access_mask, uint32_t p_post_barrier) {
 	Buffer *buffer = nullptr;
@@ -1333,7 +1335,7 @@ Error RenderingDeviceVulkan::_buffer_allocate(Buffer *p_buffer, uint32_t p_size,
 	allocInfo.requiredFlags = 0;
 	allocInfo.preferredFlags = 0;
 	allocInfo.memoryTypeBits = 0;
-	allocInfo.pool = nullptr;
+	allocInfo.pool = p_size <= SMALL_ALLOCATION_MAX_SIZE ? small_allocs_pool : nullptr;
 	allocInfo.pUserData = nullptr;
 
 	VkResult err = vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &p_buffer->buffer, &p_buffer->allocation, nullptr);
@@ -1836,13 +1838,16 @@ RID RenderingDeviceVulkan::texture_create(const TextureFormat &p_format, const T
 
 	//allocate memory
 
+	uint32_t width, height;
+	uint32_t image_size = get_image_format_required_size(p_format.format, p_format.width, p_format.height, p_format.depth, p_format.mipmaps, &width, &height);
+
 	VmaAllocationCreateInfo allocInfo;
 	allocInfo.flags = 0;
+	allocInfo.pool = image_size <= SMALL_ALLOCATION_MAX_SIZE ? small_allocs_pool : nullptr;
 	allocInfo.usage = p_format.usage_bits & TEXTURE_USAGE_CPU_READ_BIT ? VMA_MEMORY_USAGE_CPU_ONLY : VMA_MEMORY_USAGE_GPU_ONLY;
 	allocInfo.requiredFlags = 0;
 	allocInfo.preferredFlags = 0;
 	allocInfo.memoryTypeBits = 0;
-	allocInfo.pool = nullptr;
 	allocInfo.pUserData = nullptr;
 
 	Texture texture;
@@ -8808,6 +8813,18 @@ void RenderingDeviceVulkan::initialize(VulkanContext *p_context, bool p_local_de
 		vmaCreateAllocator(&allocatorInfo, &allocator);
 	}
 
+	{ //create pool for small objects
+		VmaPoolCreateInfo pci;
+		pci.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
+		pci.blockSize = 0;
+		pci.minBlockCount = 0;
+		pci.maxBlockCount = SIZE_MAX;
+		pci.priority = 0.5f;
+		pci.minAllocationAlignment = 0;
+		pci.pMemoryAllocateNext = nullptr;
+		vmaCreatePool(allocator, &pci, &small_allocs_pool);
+	}
+
 	frames = memnew_arr(Frame, frame_count);
 	frame = 0;
 	//create setup and frame buffers
@@ -9276,6 +9293,7 @@ void RenderingDeviceVulkan::finalize() {
 	for (int i = 0; i < staging_buffer_blocks.size(); i++) {
 		vmaDestroyBuffer(allocator, staging_buffer_blocks[i].buffer, staging_buffer_blocks[i].allocation);
 	}
+	vmaDestroyPool(allocator, small_allocs_pool);
 	vmaDestroyAllocator(allocator);
 
 	while (vertex_formats.size()) {

+ 1 - 0
drivers/vulkan/rendering_device_vulkan.h

@@ -1016,6 +1016,7 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	void _free_pending_resources(int p_frame);
 
 	VmaAllocator allocator = nullptr;
+	VmaPool small_allocs_pool = nullptr;
 
 	VulkanContext *context = nullptr;
 

+ 567 - 0
thirdparty/vulkan/patches/03-VMA-universal-pools.patch

@@ -0,0 +1,567 @@
+diff --git a/thirdparty/vulkan/vk_mem_alloc.h b/thirdparty/vulkan/vk_mem_alloc.h
+index 74c66b9789..89e00e6326 100644
+--- a/thirdparty/vulkan/vk_mem_alloc.h
++++ b/thirdparty/vulkan/vk_mem_alloc.h
+@@ -1127,31 +1127,26 @@ typedef struct VmaAllocationCreateInfo
+     /** \brief Intended usage of memory.
+ 
+     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
+-    If `pool` is not null, this member is ignored.
+     */
+     VmaMemoryUsage usage;
+     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
+ 
+-    Leave 0 if you specify memory requirements in other way. \n
+-    If `pool` is not null, this member is ignored.*/
++    Leave 0 if you specify memory requirements in other way.*/
+     VkMemoryPropertyFlags requiredFlags;
+     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
+ 
+-    Set to 0 if no additional flags are preferred. \n
+-    If `pool` is not null, this member is ignored. */
++    Set to 0 if no additional flags are preferred.*/
+     VkMemoryPropertyFlags preferredFlags;
+     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
+ 
+     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
+     it meets other requirements specified by this structure, with no further
+     restrictions on memory type index. \n
+-    If `pool` is not null, this member is ignored.
+     */
+     uint32_t memoryTypeBits;
+     /** \brief Pool that this allocation should be created in.
+ 
+-    Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
+-    `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
++    Leave `VK_NULL_HANDLE` to allocate from default pool.
+     */
+     VmaPool VMA_NULLABLE pool;
+     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
+@@ -1173,9 +1168,6 @@ typedef struct VmaAllocationCreateInfo
+ /// Describes parameter of created #VmaPool.
+ typedef struct VmaPoolCreateInfo
+ {
+-    /** \brief Vulkan memory type index to allocate this pool from.
+-    */
+-    uint32_t memoryTypeIndex;
+     /** \brief Use combination of #VmaPoolCreateFlagBits.
+     */
+     VmaPoolCreateFlags flags;
+@@ -10904,13 +10896,12 @@ struct VmaPool_T
+     friend struct VmaPoolListItemTraits;
+     VMA_CLASS_NO_COPY(VmaPool_T)
+ public:
+-    VmaBlockVector m_BlockVector;
+-    VmaDedicatedAllocationList m_DedicatedAllocations;
++    VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
++    VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
+ 
+     VmaPool_T(
+         VmaAllocator hAllocator,
+-        const VmaPoolCreateInfo& createInfo,
+-        VkDeviceSize preferredBlockSize);
++        const VmaPoolCreateInfo& createInfo);
+     ~VmaPool_T();
+ 
+     uint32_t GetId() const { return m_Id; }
+@@ -10924,6 +10915,7 @@ public:
+ #endif
+ 
+ private:
++    const VmaAllocator m_hAllocator;
+     uint32_t m_Id;
+     char* m_Name;
+     VmaPool_T* m_PrevPool = VMA_NULL;
+@@ -11405,8 +11397,10 @@ private:
+ 
+     void ValidateVulkanFunctions();
+ 
++public: // I'm sorry
+     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
+ 
++private:
+     VkResult AllocateMemoryOfType(
+         VmaPool pool,
+         VkDeviceSize size,
+@@ -14176,30 +14170,36 @@ void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pP
+     {
+         VmaPool pool = pPools[poolIndex];
+         VMA_ASSERT(pool);
+-        // Pools with algorithm other than default are not defragmented.
+-        if (pool->m_BlockVector.GetAlgorithm() == 0)
++        for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
+         {
+-            VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+-
+-            for (size_t i = m_CustomPoolContexts.size(); i--; )
++            if(pool->m_pBlockVectors[memTypeIndex])
+             {
+-                if (m_CustomPoolContexts[i]->GetCustomPool() == pool)
++                // Pools with algorithm other than default are not defragmented.
++                if (pool->m_pBlockVectors[memTypeIndex]->GetAlgorithm() == 0)
+                 {
+-                    pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+-                    break;
+-                }
+-            }
++                    VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
+ 
+-            if (!pBlockVectorDefragCtx)
+-            {
+-                pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+-                    m_hAllocator,
+-                    pool,
+-                    &pool->m_BlockVector);
+-                m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+-            }
++                    for (size_t i = m_CustomPoolContexts.size(); i--; )
++                    {
++                        if (m_CustomPoolContexts[i]->GetCustomPool() == pool)
++                        {
++                            pBlockVectorDefragCtx = m_CustomPoolContexts[i];
++                            break;
++                        }
++                    }
++
++                    if (!pBlockVectorDefragCtx)
++                    {
++                        pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
++                            m_hAllocator,
++                            pool,
++                            pool->m_pBlockVectors[memTypeIndex]);
++                        m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
++                    }
+ 
+-            pBlockVectorDefragCtx->AddAll();
++                    pBlockVectorDefragCtx->AddAll();
++                }
++            }
+         }
+     }
+ }
+@@ -14214,6 +14214,7 @@ void VmaDefragmentationContext_T::AddAllocations(
+     {
+         const VmaAllocation hAlloc = pAllocations[allocIndex];
+         VMA_ASSERT(hAlloc);
++        const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
+         // DedicatedAlloc cannot be defragmented.
+         if (hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK)
+         {
+@@ -14224,7 +14225,7 @@ void VmaDefragmentationContext_T::AddAllocations(
+             if (hAllocPool != VK_NULL_HANDLE)
+             {
+                 // Pools with algorithm other than default are not defragmented.
+-                if (hAllocPool->m_BlockVector.GetAlgorithm() == 0)
++                if (hAllocPool->m_pBlockVectors[memTypeIndex]->GetAlgorithm() == 0)
+                 {
+                     for (size_t i = m_CustomPoolContexts.size(); i--; )
+                     {
+@@ -14239,7 +14240,7 @@ void VmaDefragmentationContext_T::AddAllocations(
+                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+                             m_hAllocator,
+                             hAllocPool,
+-                            &hAllocPool->m_BlockVector);
++                            hAllocPool->m_pBlockVectors[memTypeIndex]);
+                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+                     }
+                 }
+@@ -14247,7 +14248,6 @@ void VmaDefragmentationContext_T::AddAllocations(
+             // This allocation belongs to default pool.
+             else
+             {
+-                const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
+                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
+                 if (!pBlockVectorDefragCtx)
+                 {
+@@ -14481,41 +14481,61 @@ VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
+ #ifndef _VMA_POOL_T_FUNCTIONS
+ VmaPool_T::VmaPool_T(
+     VmaAllocator hAllocator,
+-    const VmaPoolCreateInfo& createInfo,
+-    VkDeviceSize preferredBlockSize)
+-    : m_BlockVector(
+-        hAllocator,
+-        this, // hParentPool
+-        createInfo.memoryTypeIndex,
+-        createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
+-        createInfo.minBlockCount,
+-        createInfo.maxBlockCount,
+-        (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
+-        createInfo.blockSize != 0, // explicitBlockSize
+-        createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
+-        createInfo.priority,
+-        VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
+-        createInfo.pMemoryAllocateNext),
++    const VmaPoolCreateInfo& createInfo) :
++    m_hAllocator(hAllocator),
++    m_pBlockVectors{},
+     m_Id(0),
+-    m_Name(VMA_NULL) {}
++    m_Name(VMA_NULL)
++{
++    for(uint32_t memTypeIndex = 0; memTypeIndex < hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
++    {
++        // Create only supported types
++        if((hAllocator->GetGlobalMemoryTypeBits() & (1u << memTypeIndex)) != 0)
++        {
++            m_pBlockVectors[memTypeIndex] = vma_new(hAllocator, VmaBlockVector)(
++                hAllocator,
++                this, // hParentPool
++                memTypeIndex,
++                createInfo.blockSize != 0 ? createInfo.blockSize : hAllocator->CalcPreferredBlockSize(memTypeIndex),
++                createInfo.minBlockCount,
++                createInfo.maxBlockCount,
++                (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
++                false, // explicitBlockSize
++                createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
++                createInfo.priority,
++                VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(memTypeIndex), createInfo.minAllocationAlignment),
++                createInfo.pMemoryAllocateNext);
++        }
++    }
++}
+ 
+ VmaPool_T::~VmaPool_T()
+ {
+     VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
++    for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
++    {
++        vma_delete(m_hAllocator, m_pBlockVectors[memTypeIndex]);
++    }
+ }
+ 
+ void VmaPool_T::SetName(const char* pName)
+ {
+-    const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
+-    VmaFreeString(allocs, m_Name);
+-
+-    if (pName != VMA_NULL)
+-    {
+-        m_Name = VmaCreateStringCopy(allocs, pName);
+-    }
+-    else
++    for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
+     {
+-        m_Name = VMA_NULL;
++        if(m_pBlockVectors[memTypeIndex])
++        {
++            const VkAllocationCallbacks* allocs = m_pBlockVectors[memTypeIndex]->GetAllocator()->GetAllocationCallbacks();
++            VmaFreeString(allocs, m_Name);
++
++            if (pName != VMA_NULL)
++            {
++                m_Name = VmaCreateStringCopy(allocs, pName);
++            }
++            else
++            {
++                m_Name = VMA_NULL;
++            }
++        }
+     }
+ }
+ #endif // _VMA_POOL_T_FUNCTIONS
+@@ -15377,15 +15397,22 @@ VkResult VmaAllocator_T::CalcAllocationParams(
+         inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
+     }
+ 
+-    if(inoutCreateInfo.pool != VK_NULL_HANDLE)
++    if(inoutCreateInfo.pool != VK_NULL_HANDLE && (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
+     {
+-        if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
+-            (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
++        // Assuming here every block has the same block size and priority.
++        for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+         {
+-            VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
+-            return VK_ERROR_FEATURE_NOT_PRESENT;
++            if(inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex])
++            {
++                if(inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex]->HasExplicitBlockSize())
++                {
++                    VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
++                    return VK_ERROR_FEATURE_NOT_PRESENT;
++                }
++                inoutCreateInfo.priority = inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex]->GetPriority();
++                break;
++            }
+         }
+-        inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
+     }
+ 
+     if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
+@@ -15429,67 +15456,46 @@ VkResult VmaAllocator_T::AllocateMemory(
+     if(res != VK_SUCCESS)
+         return res;
+ 
+-    if(createInfoFinal.pool != VK_NULL_HANDLE)
++    // Bit mask of memory Vulkan types acceptable for this allocation.
++    uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
++    uint32_t memTypeIndex = UINT32_MAX;
++    res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
++    // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
++    if(res != VK_SUCCESS)
++        return res;
++    do
+     {
+-        VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
+-        return AllocateMemoryOfType(
++        VmaBlockVector* blockVector = createInfoFinal.pool == VK_NULL_HANDLE ? m_pBlockVectors[memTypeIndex] : createInfoFinal.pool->m_pBlockVectors[memTypeIndex];
++        VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
++        VmaDedicatedAllocationList& dedicatedAllocations = createInfoFinal.pool == VK_NULL_HANDLE ? m_DedicatedAllocations[memTypeIndex] : createInfoFinal.pool->m_DedicatedAllocations[memTypeIndex];
++        res = AllocateMemoryOfType(
+             createInfoFinal.pool,
+             vkMemReq.size,
+             vkMemReq.alignment,
+-            prefersDedicatedAllocation,
++            requiresDedicatedAllocation || prefersDedicatedAllocation,
+             dedicatedBuffer,
+             dedicatedBufferUsage,
+             dedicatedImage,
+             createInfoFinal,
+-            blockVector.GetMemoryTypeIndex(),
++            memTypeIndex,
+             suballocType,
+-            createInfoFinal.pool->m_DedicatedAllocations,
+-            blockVector,
++            dedicatedAllocations,
++            *blockVector,
+             allocationCount,
+             pAllocations);
+-    }
+-    else
+-    {
+-        // Bit mask of memory Vulkan types acceptable for this allocation.
+-        uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
+-        uint32_t memTypeIndex = UINT32_MAX;
+-        res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
+-        // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
+-        if(res != VK_SUCCESS)
+-            return res;
+-        do
+-        {
+-            VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
+-            VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
+-            res = AllocateMemoryOfType(
+-                VK_NULL_HANDLE,
+-                vkMemReq.size,
+-                vkMemReq.alignment,
+-                requiresDedicatedAllocation || prefersDedicatedAllocation,
+-                dedicatedBuffer,
+-                dedicatedBufferUsage,
+-                dedicatedImage,
+-                createInfoFinal,
+-                memTypeIndex,
+-                suballocType,
+-                m_DedicatedAllocations[memTypeIndex],
+-                *blockVector,
+-                allocationCount,
+-                pAllocations);
+-            // Allocation succeeded
+-            if(res == VK_SUCCESS)
+-                return VK_SUCCESS;
++        // Allocation succeeded
++        if(res == VK_SUCCESS)
++            return VK_SUCCESS;
+ 
+-            // Remove old memTypeIndex from list of possibilities.
+-            memoryTypeBits &= ~(1u << memTypeIndex);
+-            // Find alternative memTypeIndex.
+-            res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
+-        } while(res == VK_SUCCESS);
++        // Remove old memTypeIndex from list of possibilities.
++        memoryTypeBits &= ~(1u << memTypeIndex);
++        // Find alternative memTypeIndex.
++        res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
++    } while(res == VK_SUCCESS);
+ 
+-        // No other matching memory type index could be found.
+-        // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
+-        return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+-    }
++    // No other matching memory type index could be found.
++    // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
++    return VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ }
+ 
+ void VmaAllocator_T::FreeMemory(
+@@ -15515,16 +15521,16 @@ void VmaAllocator_T::FreeMemory(
+                 {
+                     VmaBlockVector* pBlockVector = VMA_NULL;
+                     VmaPool hPool = allocation->GetParentPool();
++                    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+                     if(hPool != VK_NULL_HANDLE)
+                     {
+-                        pBlockVector = &hPool->m_BlockVector;
++                        pBlockVector = hPool->m_pBlockVectors[memTypeIndex];
+                     }
+                     else
+                     {
+-                        const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
+                         pBlockVector = m_pBlockVectors[memTypeIndex];
+-                        VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
+                     }
++                    VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
+                     pBlockVector->Free(allocation);
+                 }
+                 break;
+@@ -15564,11 +15570,17 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats)
+         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
+         {
+-            VmaBlockVector& blockVector = pool->m_BlockVector;
+-            blockVector.AddStats(pStats);
+-            const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
+-            const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+-            pool->m_DedicatedAllocations.AddStats(pStats, memTypeIndex, memHeapIndex);
++            for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
++            {
++                if (pool->m_pBlockVectors[memTypeIndex])
++                {
++                    VmaBlockVector& blockVector = *pool->m_pBlockVectors[memTypeIndex];
++                    blockVector.AddStats(pStats);
++                    const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
++                    const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
++                    pool->m_DedicatedAllocations[memTypeIndex].AddStats(pStats, memTypeIndex, memHeapIndex);
++                }
++            }
+         }
+     }
+ 
+@@ -15720,27 +15732,26 @@ VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPoo
+     {
+         return VK_ERROR_INITIALIZATION_FAILED;
+     }
+-    // Memory type index out of range or forbidden.
+-    if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
+-        ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
+-    {
+-        return VK_ERROR_FEATURE_NOT_PRESENT;
+-    }
+     if(newCreateInfo.minAllocationAlignment > 0)
+     {
+         VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
+     }
+ 
+-    const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
+-
+-    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
++    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo);
+ 
+-    VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
+-    if(res != VK_SUCCESS)
++    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+     {
+-        vma_delete(this, *pPool);
+-        *pPool = VMA_NULL;
+-        return res;
++        // Create only supported types
++        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
++        {
++            VkResult res = (*pPool)->m_pBlockVectors[memTypeIndex]->CreateMinBlocks();
++            if(res != VK_SUCCESS)
++            {
++                vma_delete(this, *pPool);
++                *pPool = VMA_NULL;
++                return res;
++            }
++        }
+     }
+ 
+     // Add to m_Pools.
+@@ -15772,8 +15783,14 @@ void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
+     pPoolStats->unusedRangeCount = 0;
+     pPoolStats->blockCount = 0;
+ 
+-    pool->m_BlockVector.AddPoolStats(pPoolStats);
+-    pool->m_DedicatedAllocations.AddPoolStats(pPoolStats);
++    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
++    {
++        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
++        {
++            pool->m_pBlockVectors[memTypeIndex]->AddPoolStats(pPoolStats);
++            pool->m_DedicatedAllocations[memTypeIndex].AddPoolStats(pPoolStats);
++        }
++    }
+ }
+ 
+ void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
+@@ -15790,7 +15807,13 @@ void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
+ 
+ VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
+ {
+-    return hPool->m_BlockVector.CheckCorruption();
++    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
++    {
++        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
++        {
++            return hPool->m_pBlockVectors[memTypeIndex]->CheckCorruption();
++        }
++    }
+ }
+ 
+ VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
+@@ -15822,18 +15845,21 @@ VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
+         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
+         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
+         {
+-            if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
++            for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+             {
+-                VkResult localRes = pool->m_BlockVector.CheckCorruption();
+-                switch(localRes)
++                if(pool->m_pBlockVectors[memTypeIndex] && ((1u << memTypeIndex) & memoryTypeBits) != 0)
+                 {
+-                case VK_ERROR_FEATURE_NOT_PRESENT:
+-                    break;
+-                case VK_SUCCESS:
+-                    finalRes = VK_SUCCESS;
+-                    break;
+-                default:
+-                    return localRes;
++                    VkResult localRes = pool->m_pBlockVectors[memTypeIndex]->CheckCorruption();
++                    switch(localRes)
++                    {
++                    case VK_ERROR_FEATURE_NOT_PRESENT:
++                        break;
++                    case VK_SUCCESS:
++                        finalRes = VK_SUCCESS;
++                        break;
++                    default:
++                        return localRes;
++                    }
+                 }
+             }
+         }
+@@ -16155,7 +16181,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
+     else
+     {
+         // Custom pool
+-        parentPool->m_DedicatedAllocations.Unregister(allocation);
++        parentPool->m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
+     }
+ 
+     VkDeviceMemory hMemory = allocation->GetMemory();
+@@ -16430,12 +16456,18 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
+                 json.EndString();
+ 
+                 json.BeginObject();
+-                pool->m_BlockVector.PrintDetailedMap(json);
+-
+-                if (!pool->m_DedicatedAllocations.IsEmpty())
++                for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+                 {
+-                    json.WriteString("DedicatedAllocations");
+-                    pool->m_DedicatedAllocations.BuildStatsString(json);
++                    if (pool->m_pBlockVectors[memTypeIndex])
++                    {
++                        pool->m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
++                    }
++
++                    if (!pool->m_DedicatedAllocations[memTypeIndex].IsEmpty())
++                    {
++                        json.WriteString("DedicatedAllocations");
++                        pool->m_DedicatedAllocations->BuildStatsString(json);
++                    }
+                 }
+                 json.EndObject();
+             }

+ 189 - 157
thirdparty/vulkan/vk_mem_alloc.h

@@ -1127,31 +1127,26 @@ typedef struct VmaAllocationCreateInfo
     /** \brief Intended usage of memory.
 
     You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n
-    If `pool` is not null, this member is ignored.
     */
     VmaMemoryUsage usage;
     /** \brief Flags that must be set in a Memory Type chosen for an allocation.
 
-    Leave 0 if you specify memory requirements in other way. \n
-    If `pool` is not null, this member is ignored.*/
+    Leave 0 if you specify memory requirements in other way.*/
     VkMemoryPropertyFlags requiredFlags;
     /** \brief Flags that preferably should be set in a memory type chosen for an allocation.
 
-    Set to 0 if no additional flags are preferred. \n
-    If `pool` is not null, this member is ignored. */
+    Set to 0 if no additional flags are preferred.*/
     VkMemoryPropertyFlags preferredFlags;
     /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation.
 
     Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if
     it meets other requirements specified by this structure, with no further
     restrictions on memory type index. \n
-    If `pool` is not null, this member is ignored.
     */
     uint32_t memoryTypeBits;
     /** \brief Pool that this allocation should be created in.
 
-    Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members:
-    `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored.
+    Leave `VK_NULL_HANDLE` to allocate from default pool.
     */
     VmaPool VMA_NULLABLE pool;
     /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData().
@@ -1173,9 +1168,6 @@ typedef struct VmaAllocationCreateInfo
 /// Describes parameter of created #VmaPool.
 typedef struct VmaPoolCreateInfo
 {
-    /** \brief Vulkan memory type index to allocate this pool from.
-    */
-    uint32_t memoryTypeIndex;
     /** \brief Use combination of #VmaPoolCreateFlagBits.
     */
     VmaPoolCreateFlags flags;
@@ -10904,13 +10896,12 @@ struct VmaPool_T
     friend struct VmaPoolListItemTraits;
     VMA_CLASS_NO_COPY(VmaPool_T)
 public:
-    VmaBlockVector m_BlockVector;
-    VmaDedicatedAllocationList m_DedicatedAllocations;
+    VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES];
+    VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES];
 
     VmaPool_T(
         VmaAllocator hAllocator,
-        const VmaPoolCreateInfo& createInfo,
-        VkDeviceSize preferredBlockSize);
+        const VmaPoolCreateInfo& createInfo);
     ~VmaPool_T();
 
     uint32_t GetId() const { return m_Id; }
@@ -10924,6 +10915,7 @@ public:
 #endif
 
 private:
+    const VmaAllocator m_hAllocator;
     uint32_t m_Id;
     char* m_Name;
     VmaPool_T* m_PrevPool = VMA_NULL;
@@ -11405,8 +11397,10 @@ private:
 
     void ValidateVulkanFunctions();
 
+public: // I'm sorry
     VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex);
 
+private:
     VkResult AllocateMemoryOfType(
         VmaPool pool,
         VkDeviceSize size,
@@ -14176,30 +14170,36 @@ void VmaDefragmentationContext_T::AddPools(uint32_t poolCount, const VmaPool* pP
     {
         VmaPool pool = pPools[poolIndex];
         VMA_ASSERT(pool);
-        // Pools with algorithm other than default are not defragmented.
-        if (pool->m_BlockVector.GetAlgorithm() == 0)
+        for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
         {
-            VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
-
-            for (size_t i = m_CustomPoolContexts.size(); i--; )
+            if(pool->m_pBlockVectors[memTypeIndex])
             {
-                if (m_CustomPoolContexts[i]->GetCustomPool() == pool)
+                // Pools with algorithm other than default are not defragmented.
+                if (pool->m_pBlockVectors[memTypeIndex]->GetAlgorithm() == 0)
                 {
-                    pBlockVectorDefragCtx = m_CustomPoolContexts[i];
-                    break;
-                }
-            }
+                    VmaBlockVectorDefragmentationContext* pBlockVectorDefragCtx = VMA_NULL;
 
-            if (!pBlockVectorDefragCtx)
-            {
-                pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
-                    m_hAllocator,
-                    pool,
-                    &pool->m_BlockVector);
-                m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
-            }
+                    for (size_t i = m_CustomPoolContexts.size(); i--; )
+                    {
+                        if (m_CustomPoolContexts[i]->GetCustomPool() == pool)
+                        {
+                            pBlockVectorDefragCtx = m_CustomPoolContexts[i];
+                            break;
+                        }
+                    }
+
+                    if (!pBlockVectorDefragCtx)
+                    {
+                        pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
+                            m_hAllocator,
+                            pool,
+                            pool->m_pBlockVectors[memTypeIndex]);
+                        m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
+                    }
 
-            pBlockVectorDefragCtx->AddAll();
+                    pBlockVectorDefragCtx->AddAll();
+                }
+            }
         }
     }
 }
@@ -14214,6 +14214,7 @@ void VmaDefragmentationContext_T::AddAllocations(
     {
         const VmaAllocation hAlloc = pAllocations[allocIndex];
         VMA_ASSERT(hAlloc);
+        const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
         // DedicatedAlloc cannot be defragmented.
         if (hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK)
         {
@@ -14224,7 +14225,7 @@ void VmaDefragmentationContext_T::AddAllocations(
             if (hAllocPool != VK_NULL_HANDLE)
             {
                 // Pools with algorithm other than default are not defragmented.
-                if (hAllocPool->m_BlockVector.GetAlgorithm() == 0)
+                if (hAllocPool->m_pBlockVectors[memTypeIndex]->GetAlgorithm() == 0)
                 {
                     for (size_t i = m_CustomPoolContexts.size(); i--; )
                     {
@@ -14239,7 +14240,7 @@ void VmaDefragmentationContext_T::AddAllocations(
                         pBlockVectorDefragCtx = vma_new(m_hAllocator, VmaBlockVectorDefragmentationContext)(
                             m_hAllocator,
                             hAllocPool,
-                            &hAllocPool->m_BlockVector);
+                            hAllocPool->m_pBlockVectors[memTypeIndex]);
                         m_CustomPoolContexts.push_back(pBlockVectorDefragCtx);
                     }
                 }
@@ -14247,7 +14248,6 @@ void VmaDefragmentationContext_T::AddAllocations(
             // This allocation belongs to default pool.
             else
             {
-                const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
                 pBlockVectorDefragCtx = m_DefaultPoolContexts[memTypeIndex];
                 if (!pBlockVectorDefragCtx)
                 {
@@ -14481,41 +14481,61 @@ VkResult VmaDefragmentationContext_T::DefragmentPassEnd()
 #ifndef _VMA_POOL_T_FUNCTIONS
 VmaPool_T::VmaPool_T(
     VmaAllocator hAllocator,
-    const VmaPoolCreateInfo& createInfo,
-    VkDeviceSize preferredBlockSize)
-    : m_BlockVector(
-        hAllocator,
-        this, // hParentPool
-        createInfo.memoryTypeIndex,
-        createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize,
-        createInfo.minBlockCount,
-        createInfo.maxBlockCount,
-        (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
-        createInfo.blockSize != 0, // explicitBlockSize
-        createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
-        createInfo.priority,
-        VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment),
-        createInfo.pMemoryAllocateNext),
+    const VmaPoolCreateInfo& createInfo) :
+    m_hAllocator(hAllocator),
+    m_pBlockVectors{},
     m_Id(0),
-    m_Name(VMA_NULL) {}
+    m_Name(VMA_NULL)
+{
+    for(uint32_t memTypeIndex = 0; memTypeIndex < hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
+    {
+        // Create only supported types
+        if((hAllocator->GetGlobalMemoryTypeBits() & (1u << memTypeIndex)) != 0)
+        {
+            m_pBlockVectors[memTypeIndex] = vma_new(hAllocator, VmaBlockVector)(
+                hAllocator,
+                this, // hParentPool
+                memTypeIndex,
+                createInfo.blockSize != 0 ? createInfo.blockSize : hAllocator->CalcPreferredBlockSize(memTypeIndex),
+                createInfo.minBlockCount,
+                createInfo.maxBlockCount,
+                (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(),
+                false, // explicitBlockSize
+                createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm
+                createInfo.priority,
+                VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(memTypeIndex), createInfo.minAllocationAlignment),
+                createInfo.pMemoryAllocateNext);
+        }
+    }
+}
 
 VmaPool_T::~VmaPool_T()
 {
     VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL);
+    for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
+    {
+        vma_delete(m_hAllocator, m_pBlockVectors[memTypeIndex]);
+    }
 }
 
 void VmaPool_T::SetName(const char* pName)
 {
-    const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks();
-    VmaFreeString(allocs, m_Name);
-
-    if (pName != VMA_NULL)
-    {
-        m_Name = VmaCreateStringCopy(allocs, pName);
-    }
-    else
+    for(uint32_t memTypeIndex = 0; memTypeIndex < m_hAllocator->GetMemoryTypeCount(); ++memTypeIndex)
     {
-        m_Name = VMA_NULL;
+        if(m_pBlockVectors[memTypeIndex])
+        {
+            const VkAllocationCallbacks* allocs = m_pBlockVectors[memTypeIndex]->GetAllocator()->GetAllocationCallbacks();
+            VmaFreeString(allocs, m_Name);
+
+            if (pName != VMA_NULL)
+            {
+                m_Name = VmaCreateStringCopy(allocs, pName);
+            }
+            else
+            {
+                m_Name = VMA_NULL;
+            }
+        }
     }
 }
 #endif // _VMA_POOL_T_FUNCTIONS
@@ -15377,15 +15397,22 @@ VkResult VmaAllocator_T::CalcAllocationParams(
         inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
     }
 
-    if(inoutCreateInfo.pool != VK_NULL_HANDLE)
+    if(inoutCreateInfo.pool != VK_NULL_HANDLE && (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
     {
-        if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() &&
-            (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0)
+        // Assuming here every block has the same block size and priority.
+        for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
         {
-            VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
-            return VK_ERROR_FEATURE_NOT_PRESENT;
+            if(inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex])
+            {
+                if(inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex]->HasExplicitBlockSize())
+                {
+                    VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations.");
+                    return VK_ERROR_FEATURE_NOT_PRESENT;
+                }
+                inoutCreateInfo.priority = inoutCreateInfo.pool->m_pBlockVectors[memTypeIndex]->GetPriority();
+                break;
+            }
         }
-        inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority();
     }
 
     if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 &&
@@ -15429,67 +15456,46 @@ VkResult VmaAllocator_T::AllocateMemory(
     if(res != VK_SUCCESS)
         return res;
 
-    if(createInfoFinal.pool != VK_NULL_HANDLE)
+    // Bit mask of memory Vulkan types acceptable for this allocation.
+    uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
+    uint32_t memTypeIndex = UINT32_MAX;
+    res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
+    // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
+    if(res != VK_SUCCESS)
+        return res;
+    do
     {
-        VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector;
-        return AllocateMemoryOfType(
+        VmaBlockVector* blockVector = createInfoFinal.pool == VK_NULL_HANDLE ? m_pBlockVectors[memTypeIndex] : createInfoFinal.pool->m_pBlockVectors[memTypeIndex];
+        VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
+        VmaDedicatedAllocationList& dedicatedAllocations = createInfoFinal.pool == VK_NULL_HANDLE ? m_DedicatedAllocations[memTypeIndex] : createInfoFinal.pool->m_DedicatedAllocations[memTypeIndex];
+        res = AllocateMemoryOfType(
             createInfoFinal.pool,
             vkMemReq.size,
             vkMemReq.alignment,
-            prefersDedicatedAllocation,
+            requiresDedicatedAllocation || prefersDedicatedAllocation,
             dedicatedBuffer,
             dedicatedBufferUsage,
             dedicatedImage,
             createInfoFinal,
-            blockVector.GetMemoryTypeIndex(),
+            memTypeIndex,
             suballocType,
-            createInfoFinal.pool->m_DedicatedAllocations,
-            blockVector,
+            dedicatedAllocations,
+            *blockVector,
             allocationCount,
             pAllocations);
-    }
-    else
-    {
-        // Bit mask of memory Vulkan types acceptable for this allocation.
-        uint32_t memoryTypeBits = vkMemReq.memoryTypeBits;
-        uint32_t memTypeIndex = UINT32_MAX;
-        res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
-        // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT.
-        if(res != VK_SUCCESS)
-            return res;
-        do
-        {
-            VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex];
-            VMA_ASSERT(blockVector && "Trying to use unsupported memory type!");
-            res = AllocateMemoryOfType(
-                VK_NULL_HANDLE,
-                vkMemReq.size,
-                vkMemReq.alignment,
-                requiresDedicatedAllocation || prefersDedicatedAllocation,
-                dedicatedBuffer,
-                dedicatedBufferUsage,
-                dedicatedImage,
-                createInfoFinal,
-                memTypeIndex,
-                suballocType,
-                m_DedicatedAllocations[memTypeIndex],
-                *blockVector,
-                allocationCount,
-                pAllocations);
-            // Allocation succeeded
-            if(res == VK_SUCCESS)
-                return VK_SUCCESS;
+        // Allocation succeeded
+        if(res == VK_SUCCESS)
+            return VK_SUCCESS;
 
-            // Remove old memTypeIndex from list of possibilities.
-            memoryTypeBits &= ~(1u << memTypeIndex);
-            // Find alternative memTypeIndex.
-            res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
-        } while(res == VK_SUCCESS);
+        // Remove old memTypeIndex from list of possibilities.
+        memoryTypeBits &= ~(1u << memTypeIndex);
+        // Find alternative memTypeIndex.
+        res = vmaFindMemoryTypeIndex(this, memoryTypeBits, &createInfoFinal, &memTypeIndex);
+    } while(res == VK_SUCCESS);
 
-        // No other matching memory type index could be found.
-        // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
-        return VK_ERROR_OUT_OF_DEVICE_MEMORY;
-    }
+    // No other matching memory type index could be found.
+    // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once.
+    return VK_ERROR_OUT_OF_DEVICE_MEMORY;
 }
 
 void VmaAllocator_T::FreeMemory(
@@ -15515,16 +15521,16 @@ void VmaAllocator_T::FreeMemory(
                 {
                     VmaBlockVector* pBlockVector = VMA_NULL;
                     VmaPool hPool = allocation->GetParentPool();
+                    const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
                     if(hPool != VK_NULL_HANDLE)
                     {
-                        pBlockVector = &hPool->m_BlockVector;
+                        pBlockVector = hPool->m_pBlockVectors[memTypeIndex];
                     }
                     else
                     {
-                        const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex();
                         pBlockVector = m_pBlockVectors[memTypeIndex];
-                        VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
                     }
+                    VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!");
                     pBlockVector->Free(allocation);
                 }
                 break;
@@ -15564,11 +15570,17 @@ void VmaAllocator_T::CalculateStats(VmaStats* pStats)
         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
         {
-            VmaBlockVector& blockVector = pool->m_BlockVector;
-            blockVector.AddStats(pStats);
-            const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
-            const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
-            pool->m_DedicatedAllocations.AddStats(pStats, memTypeIndex, memHeapIndex);
+            for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+            {
+                if (pool->m_pBlockVectors[memTypeIndex])
+                {
+                    VmaBlockVector& blockVector = *pool->m_pBlockVectors[memTypeIndex];
+                    blockVector.AddStats(pStats);
+                    const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex();
+                    const uint32_t memHeapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex);
+                    pool->m_DedicatedAllocations[memTypeIndex].AddStats(pStats, memTypeIndex, memHeapIndex);
+                }
+            }
         }
     }
 
@@ -15720,27 +15732,26 @@ VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPoo
     {
         return VK_ERROR_INITIALIZATION_FAILED;
     }
-    // Memory type index out of range or forbidden.
-    if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() ||
-        ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0)
-    {
-        return VK_ERROR_FEATURE_NOT_PRESENT;
-    }
     if(newCreateInfo.minAllocationAlignment > 0)
     {
         VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment));
     }
 
-    const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex);
-
-    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize);
+    *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo);
 
-    VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks();
-    if(res != VK_SUCCESS)
+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
     {
-        vma_delete(this, *pPool);
-        *pPool = VMA_NULL;
-        return res;
+        // Create only supported types
+        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
+        {
+            VkResult res = (*pPool)->m_pBlockVectors[memTypeIndex]->CreateMinBlocks();
+            if(res != VK_SUCCESS)
+            {
+                vma_delete(this, *pPool);
+                *pPool = VMA_NULL;
+                return res;
+            }
+        }
     }
 
     // Add to m_Pools.
@@ -15772,8 +15783,14 @@ void VmaAllocator_T::GetPoolStats(VmaPool pool, VmaPoolStats* pPoolStats)
     pPoolStats->unusedRangeCount = 0;
     pPoolStats->blockCount = 0;
 
-    pool->m_BlockVector.AddPoolStats(pPoolStats);
-    pool->m_DedicatedAllocations.AddPoolStats(pPoolStats);
+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+    {
+        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
+        {
+            pool->m_pBlockVectors[memTypeIndex]->AddPoolStats(pPoolStats);
+            pool->m_DedicatedAllocations[memTypeIndex].AddPoolStats(pPoolStats);
+        }
+    }
 }
 
 void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
@@ -15790,7 +15807,13 @@ void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex)
 
 VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool)
 {
-    return hPool->m_BlockVector.CheckCorruption();
+    for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
+    {
+        if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0)
+        {
+            return hPool->m_pBlockVectors[memTypeIndex]->CheckCorruption();
+        }
+    }
 }
 
 VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
@@ -15822,18 +15845,21 @@ VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits)
         VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex);
         for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool))
         {
-            if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0)
+            for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
             {
-                VkResult localRes = pool->m_BlockVector.CheckCorruption();
-                switch(localRes)
+                if(pool->m_pBlockVectors[memTypeIndex] && ((1u << memTypeIndex) & memoryTypeBits) != 0)
                 {
-                case VK_ERROR_FEATURE_NOT_PRESENT:
-                    break;
-                case VK_SUCCESS:
-                    finalRes = VK_SUCCESS;
-                    break;
-                default:
-                    return localRes;
+                    VkResult localRes = pool->m_pBlockVectors[memTypeIndex]->CheckCorruption();
+                    switch(localRes)
+                    {
+                    case VK_ERROR_FEATURE_NOT_PRESENT:
+                        break;
+                    case VK_SUCCESS:
+                        finalRes = VK_SUCCESS;
+                        break;
+                    default:
+                        return localRes;
+                    }
                 }
             }
         }
@@ -16155,7 +16181,7 @@ void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
     else
     {
         // Custom pool
-        parentPool->m_DedicatedAllocations.Unregister(allocation);
+        parentPool->m_DedicatedAllocations[memTypeIndex].Unregister(allocation);
     }
 
     VkDeviceMemory hMemory = allocation->GetMemory();
@@ -16430,12 +16456,18 @@ void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json)
                 json.EndString();
 
                 json.BeginObject();
-                pool->m_BlockVector.PrintDetailedMap(json);
-
-                if (!pool->m_DedicatedAllocations.IsEmpty())
+                for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex)
                 {
-                    json.WriteString("DedicatedAllocations");
-                    pool->m_DedicatedAllocations.BuildStatsString(json);
+                    if (pool->m_pBlockVectors[memTypeIndex])
+                    {
+                        pool->m_pBlockVectors[memTypeIndex]->PrintDetailedMap(json);
+                    }
+
+                    if (!pool->m_DedicatedAllocations[memTypeIndex].IsEmpty())
+                    {
+                        json.WriteString("DedicatedAllocations");
+                        pool->m_DedicatedAllocations->BuildStatsString(json);
+                    }
                 }
                 json.EndObject();
             }