From 3a0817b319fe667bfd2a3d97c160acfc7afe6998 Mon Sep 17 00:00:00 2001 From: Kate Date: Sun, 5 Jul 2026 11:51:16 +0200 Subject: [PATCH 1/2] feat: add virtual block resizing --- include/vk_mem_alloc.h | 63 ++++++++++++++++++++++++++++++++++++++++++ src/Tests.cpp | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h index f2b58bf6..7cabcc2c 100644 --- a/include/vk_mem_alloc.h +++ b/include/vk_mem_alloc.h @@ -2896,6 +2896,10 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock); +VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock, + VkDeviceSize newSize); + /** \brief Destroys #VmaVirtualBlock object. Please note that you should consciously handle virtual allocations that could remain unfreed in the block. @@ -6998,6 +7002,7 @@ class VmaBlockMetadata virtual ~VmaBlockMetadata() = default; virtual void Init(VkDeviceSize size) { m_Size = size; } + virtual bool Resize(VkDeviceSize newSize) { m_Size = newSize; return true; } bool IsVirtual() const { return m_IsVirtual; } VkDeviceSize GetSize() const { return m_Size; } @@ -7548,6 +7553,7 @@ class VmaBlockMetadata_Linear : public VmaBlockMetadata VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; } void Init(VkDeviceSize size) override; + bool Resize(VkDeviceSize newSize) override; bool Validate() const override; size_t GetAllocationCount() const override; size_t GetFreeRegionsCount() const override; @@ -7663,6 +7669,18 @@ void VmaBlockMetadata_Linear::Init(VkDeviceSize size) m_SumFreeSize = size; } +bool VmaBlockMetadata_Linear::Resize(VkDeviceSize newSize) +{ + VMA_VALIDATE(newSize > GetSize()); + + const VkDeviceSize extension = newSize - GetSize(); + VmaBlockMetadata::Resize(newSize); + + m_SumFreeSize += extension; + + return true; +} + bool VmaBlockMetadata_Linear::Validate() const { const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); @@ -9165,6 +9183,7 @@ class VmaBlockMetadata_TLSF : public VmaBlockMetadata VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; } void Init(VkDeviceSize size) override; + bool Resize(VkDeviceSize newSize) override; bool Validate() const override; void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; @@ -9321,6 +9340,39 @@ void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); } +bool VmaBlockMetadata_TLSF::Resize(VkDeviceSize newSize) +{ + VMA_VALIDATE(newSize > GetSize()); + + size_t offset = GetSize(); + size_t extension = newSize - GetSize(); + VmaBlockMetadata::Resize(newSize); + + Block* newFreeBlock = m_BlockAllocator.Alloc(); + newFreeBlock->offset = offset; + newFreeBlock->size = extension; + newFreeBlock->NextFree() = VMA_NULL; + newFreeBlock->PrevFree() = VMA_NULL; + newFreeBlock->MarkTaken(); + + Block* lastPhys = m_NullBlock->prevPhysical; + newFreeBlock->prevPhysical = lastPhys; + newFreeBlock->nextPhysical = m_NullBlock; + + if (lastPhys != VMA_NULL) { + lastPhys->nextPhysical = newFreeBlock; + } + m_NullBlock->prevPhysical = newFreeBlock; + + InsertFreeBlock(newFreeBlock); + + m_NullBlock->offset = newSize; + m_NullBlock->size = 0; + + + return true; +} + bool VmaBlockMetadata_TLSF::Validate() const { VMA_VALIDATE(GetSumFreeSize() <= GetSize()); @@ -10452,6 +10504,7 @@ struct VmaVirtualBlock_T void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); } void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); } void Clear() { m_Metadata->Clear(); } + void Resize(VkDeviceSize newSize) { m_Metadata->Resize(newSize); } const VkAllocationCallbacks* GetAllocationCallbacks() const; void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo); @@ -17128,6 +17181,16 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( */ } +VMA_CALL_PRE VkResult VMA_CALL_POST vmaResizeVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock, + VkDeviceSize newSize) +{ + VMA_DEBUG_LOG("vmaResizeVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Resize(newSize); + return VK_SUCCESS; +} + VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock) { if(virtualBlock != VK_NULL_HANDLE) diff --git a/src/Tests.cpp b/src/Tests.cpp index 996b2d08..b5273d9a 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -3379,6 +3379,54 @@ static void TestVirtualBlocks() } } +static void TestVirtualBlocksResizeImpl(VmaVirtualBlockCreateFlags flags) +{ + VmaVirtualBlockCreateInfo blockCreateInfo = {}; + blockCreateInfo.size = 3; + blockCreateInfo.flags = flags; + + VmaVirtualBlock block; + TEST(vmaCreateVirtualBlock(&blockCreateInfo, &block) == VK_SUCCESS && block); + + VmaVirtualAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.size = 1; + VmaVirtualAllocation allocation0 = VK_NULL_HANDLE; + VmaVirtualAllocation allocation1 = VK_NULL_HANDLE; + VmaVirtualAllocation allocation2 = VK_NULL_HANDLE; + + VkDeviceSize offset; + + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &allocation0, &offset) == VK_SUCCESS); + TEST(allocation0 != VK_NULL_HANDLE); + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &allocation1, &offset) == VK_SUCCESS); + TEST(allocation1 != VK_NULL_HANDLE); + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &allocation2, &offset) == VK_SUCCESS); + TEST(allocation2 != VK_NULL_HANDLE); + + allocCreateInfo.size = 2; + + vmaVirtualFree(block, allocation1); + + VmaVirtualAllocation oomAllocation = VK_NULL_HANDLE; + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &oomAllocation, &offset) != VK_SUCCESS); + TEST(oomAllocation == VK_NULL_HANDLE); + + vmaResizeVirtualBlock(block, 5); + + VmaVirtualAllocation noLongerOomAllocation = VK_NULL_HANDLE; + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &noLongerOomAllocation, &offset) == VK_SUCCESS); + TEST(noLongerOomAllocation != VK_NULL_HANDLE); + + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &oomAllocation, &offset) != VK_SUCCESS); + TEST(oomAllocation == VK_NULL_HANDLE); +} + +static void TestVirtualBlocksResize() +{ + TestVirtualBlocksResizeImpl(0); + TestVirtualBlocksResizeImpl(VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT); +} + static void TestVirtualBlocksAlgorithms() { wprintf(L"Test virtual blocks algorithms\n"); @@ -8861,6 +8909,7 @@ void Test() TestJson(); TestBasics(); TestVirtualBlocks(); + TestVirtualBlocksResize(); TestVirtualBlocksAlgorithms(); TestVirtualBlocksAlgorithmsBenchmark(); TestAllocationVersusResourceSize(); From 576f2f9ba91c37d4025613807cb9239fcd2874d2 Mon Sep 17 00:00:00 2001 From: Kate Date: Sun, 5 Jul 2026 12:53:00 +0200 Subject: [PATCH 2/2] fix size class extension --- include/vk_mem_alloc.h | 63 ++++++++++++++++++++++++++++-------------- src/Tests.cpp | 24 ++++++++++++++++ 2 files changed, 67 insertions(+), 20 deletions(-) diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h index 7cabcc2c..5120056d 100644 --- a/include/vk_mem_alloc.h +++ b/include/vk_mem_alloc.h @@ -9342,33 +9342,56 @@ void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) bool VmaBlockMetadata_TLSF::Resize(VkDeviceSize newSize) { - VMA_VALIDATE(newSize > GetSize()); + VMA_ASSERT(IsVirtual()); + VMA_ASSERT(newSize > GetSize()); + + VkDeviceSize oldSize = GetSize(); + VkDeviceSize extension = newSize - oldSize; - size_t offset = GetSize(); - size_t extension = newSize - GetSize(); VmaBlockMetadata::Resize(newSize); - Block* newFreeBlock = m_BlockAllocator.Alloc(); - newFreeBlock->offset = offset; - newFreeBlock->size = extension; - newFreeBlock->NextFree() = VMA_NULL; - newFreeBlock->PrevFree() = VMA_NULL; - newFreeBlock->MarkTaken(); + uint8_t newMemoryClass = SizeToMemoryClass(newSize) + uint8_t(2); + uint16_t sli = SizeToSecondIndex(newSize, newMemoryClass); + if (newMemoryClass > m_MemoryClasses) + { + uint32_t oldListsCount = m_ListsCount; + uint32_t newListsCount = (newMemoryClass == 0 ? 0 : (newMemoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1; + + // in case of non virtual support at some point + if (IsVirtual()) + newListsCount += 1UL << SECOND_LEVEL_INDEX; + else + newListsCount += 4; - Block* lastPhys = m_NullBlock->prevPhysical; - newFreeBlock->prevPhysical = lastPhys; - newFreeBlock->nextPhysical = m_NullBlock; + Block** newFreeList = vma_new_array(GetAllocationCallbacks(), Block*, newListsCount); + VMA_ASSERT(newFreeList); - if (lastPhys != VMA_NULL) { - lastPhys->nextPhysical = newFreeBlock; - } - m_NullBlock->prevPhysical = newFreeBlock; - - InsertFreeBlock(newFreeBlock); + if (oldListsCount > 0) + { + memcpy(newFreeList, m_FreeList, oldListsCount * sizeof(Block*)); + } - m_NullBlock->offset = newSize; - m_NullBlock->size = 0; + memset(newFreeList + oldListsCount, 0, + (newListsCount - oldListsCount) * sizeof(Block*)); + vma_delete_array(GetAllocationCallbacks(), m_FreeList, oldListsCount); + m_FreeList = newFreeList; + m_ListsCount = newListsCount; + + for (uint8_t i = m_MemoryClasses; i < newMemoryClass; ++i) + { + m_InnerIsFreeBitmap[i] = 0; + } + + m_MemoryClasses = newMemoryClass; + } + + m_NullBlock->size += extension; + + if (!m_NullBlock->IsFree()) + { + m_NullBlock->MarkFree(); + } return true; } diff --git a/src/Tests.cpp b/src/Tests.cpp index b5273d9a..7a898b7e 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -3421,10 +3421,34 @@ static void TestVirtualBlocksResizeImpl(VmaVirtualBlockCreateFlags flags) TEST(oomAllocation == VK_NULL_HANDLE); } +static void TestSizeClassExtension() +{ + VmaVirtualBlockCreateInfo blockCreateInfo = {}; + blockCreateInfo.size = 40960; + + VmaVirtualBlock block; + TEST(vmaCreateVirtualBlock(&blockCreateInfo, &block) == VK_SUCCESS && block); + + VmaVirtualAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.size = 45936; + + VmaVirtualAllocation allocation0 = VK_NULL_HANDLE; + VkDeviceSize offset; + + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &allocation0, &offset) != VK_SUCCESS); + TEST(allocation0 == VK_NULL_HANDLE); + + TEST(vmaResizeVirtualBlock(block, 86896) == VK_SUCCESS); + + TEST(vmaVirtualAllocate(block, &allocCreateInfo, &allocation0, &offset) == VK_SUCCESS); + TEST(allocation0 != VK_NULL_HANDLE); +} + static void TestVirtualBlocksResize() { TestVirtualBlocksResizeImpl(0); TestVirtualBlocksResizeImpl(VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT); + TestSizeClassExtension(); } static void TestVirtualBlocksAlgorithms()