diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h index f2b58bf6..5120056d 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,62 @@ void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); } +bool VmaBlockMetadata_TLSF::Resize(VkDeviceSize newSize) +{ + VMA_ASSERT(IsVirtual()); + VMA_ASSERT(newSize > GetSize()); + + VkDeviceSize oldSize = GetSize(); + VkDeviceSize extension = newSize - oldSize; + + VmaBlockMetadata::Resize(newSize); + + 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** newFreeList = vma_new_array(GetAllocationCallbacks(), Block*, newListsCount); + VMA_ASSERT(newFreeList); + + if (oldListsCount > 0) + { + memcpy(newFreeList, m_FreeList, oldListsCount * sizeof(Block*)); + } + + 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; +} + bool VmaBlockMetadata_TLSF::Validate() const { VMA_VALIDATE(GetSumFreeSize() <= GetSize()); @@ -10452,6 +10527,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 +17204,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..7a898b7e 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -3379,6 +3379,78 @@ 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 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() { wprintf(L"Test virtual blocks algorithms\n"); @@ -8861,6 +8933,7 @@ void Test() TestJson(); TestBasics(); TestVirtualBlocks(); + TestVirtualBlocksResize(); TestVirtualBlocksAlgorithms(); TestVirtualBlocksAlgorithmsBenchmark(); TestAllocationVersusResourceSize();