From 73ff8e07d1654b25a8601553c23af1ab4504ef12 Mon Sep 17 00:00:00 2001 From: Adam Sawicki Date: Thu, 28 Oct 2021 19:20:12 +0200 Subject: [PATCH] Preparations for adding support for alternative algorithms in virtual blocks and tests for them --- include/vk_mem_alloc.h | 28 +++++---- src/Tests.cpp | 140 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 156 insertions(+), 12 deletions(-) diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h index 528480a..9201e10 100644 --- a/include/vk_mem_alloc.h +++ b/include/vk_mem_alloc.h @@ -8887,14 +8887,20 @@ bool VmaBlockMetadata_Linear::Validate() const if(!suballocations1st.empty()) { // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. - VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].userData != VMA_NULL); - // Null item at the end should be just pop_back(). - VMA_VALIDATE(suballocations1st.back().userData != VMA_NULL); + if(!IsVirtual()) + { + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].userData != VMA_NULL); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().userData != VMA_NULL); + } } if(!suballocations2nd.empty()) { - // Null item at the end should be just pop_back(). - VMA_VALIDATE(suballocations2nd.back().userData != VMA_NULL); + if(!IsVirtual()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().userData != VMA_NULL); + } } VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); @@ -10555,7 +10561,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() // Find more null items at the beginning of 1st vector. while(m_1stNullItemsBeginCount < suballoc1stCount && - suballocations1st[m_1stNullItemsBeginCount].userData == VMA_NULL) + suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) { ++m_1stNullItemsBeginCount; --m_1stNullItemsMiddleCount; @@ -10563,7 +10569,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() // Find more null items at the end of 1st vector. while(m_1stNullItemsMiddleCount > 0 && - suballocations1st.back().userData == VMA_NULL) + suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE) { --m_1stNullItemsMiddleCount; suballocations1st.pop_back(); @@ -10571,7 +10577,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() // Find more null items at the end of 2nd vector. while(m_2ndNullItemsCount > 0 && - suballocations2nd.back().userData == VMA_NULL) + suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE) { --m_2ndNullItemsCount; suballocations2nd.pop_back(); @@ -10579,7 +10585,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() // Find more null items at the beginning of 2nd vector. while(m_2ndNullItemsCount > 0 && - suballocations2nd[0].userData == VMA_NULL) + suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE) { --m_2ndNullItemsCount; VmaVectorRemove(suballocations2nd, 0); @@ -10591,7 +10597,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() size_t srcIndex = m_1stNullItemsBeginCount; for(size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) { - while(suballocations1st[srcIndex].userData == VMA_NULL) + while(suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE) { ++srcIndex; } @@ -10624,7 +10630,7 @@ void VmaBlockMetadata_Linear::CleanupAfterFree() m_2ndVectorMode = SECOND_VECTOR_EMPTY; m_1stNullItemsMiddleCount = m_2ndNullItemsCount; while(m_1stNullItemsBeginCount < suballocations2nd.size() && - suballocations2nd[m_1stNullItemsBeginCount].userData == VMA_NULL) + suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) { ++m_1stNullItemsBeginCount; --m_1stNullItemsMiddleCount; diff --git a/src/Tests.cpp b/src/Tests.cpp index db1d7e7..50b34cc 100644 --- a/src/Tests.cpp +++ b/src/Tests.cpp @@ -2817,6 +2817,144 @@ static void TestVirtualBlocks() } } +static void TestVirtualBlocksAlgorithms() +{ + wprintf(L"Test virtual blocks algorithms\n"); + + RandomNumberGenerator rand{3454335}; + auto calcRandomAllocSize = [&rand]() -> VkDeviceSize { return rand.Generate() % 20 + 5; }; + + for(size_t algorithmIndex = 0; algorithmIndex < 1/*3*/; ++algorithmIndex) + { + // Create the block + VmaVirtualBlockCreateInfo blockCreateInfo = {}; + blockCreateInfo.pAllocationCallbacks = g_Allocs; + blockCreateInfo.size = 10'000; + switch(algorithmIndex) + { + case 1: blockCreateInfo.flags = VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT; break; + case 2: blockCreateInfo.flags = VMA_VIRTUAL_BLOCK_CREATE_BUDDY_ALGORITHM_BIT; break; + } + VmaVirtualBlock block = nullptr; + VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block); + TEST(res == VK_SUCCESS); + + struct AllocData + { + VkDeviceSize offset, size; + }; + std::vector allocations; + + // Make some allocations + for(size_t i = 0; i < 20; ++i) + { + VmaVirtualAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.size = calcRandomAllocSize(); + allocCreateInfo.pUserData = (void*)(uintptr_t)(allocCreateInfo.size * 10); + if(i < 10) { } + else if(i < 12) allocCreateInfo.flags = VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT; + else if(i < 14) allocCreateInfo.flags = VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT; + else if(i < 16) allocCreateInfo.flags = VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT; + else if(i < 18 && algorithmIndex == 1) allocCreateInfo.flags = VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT; + + AllocData alloc = {}; + alloc.size = allocCreateInfo.size; + res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc.offset); + TEST(res == VK_SUCCESS); + allocations.push_back(alloc); + } + + // Free some of the allocations + for(size_t i = 0; i < 5; ++i) + { + const size_t index = rand.Generate() % allocations.size(); + vmaVirtualFree(block, allocations[index].offset); + allocations.erase(allocations.begin() + index); + } + + // Allocate some more + for(size_t i = 0; i < 6; ++i) + { + VmaVirtualAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.size = calcRandomAllocSize(); + allocCreateInfo.pUserData = (void*)(uintptr_t)(allocCreateInfo.size * 10); + + AllocData alloc = {}; + alloc.size = allocCreateInfo.size; + res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc.offset); + TEST(res == VK_SUCCESS); + allocations.push_back(alloc); + } + + // Allocate some with extra alignment + for(size_t i = 0; i < 3; ++i) + { + VmaVirtualAllocationCreateInfo allocCreateInfo = {}; + allocCreateInfo.size = calcRandomAllocSize(); + allocCreateInfo.alignment = 16; + allocCreateInfo.pUserData = (void*)(uintptr_t)(allocCreateInfo.size * 10); + + AllocData alloc = {}; + alloc.size = allocCreateInfo.size; + res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc.offset); + TEST(res == VK_SUCCESS); + TEST(alloc.offset % 16 == 0); + allocations.push_back(alloc); + } + + // Check if the allocations don't overlap + std::sort(allocations.begin(), allocations.end(), [](const AllocData& lhs, const AllocData& rhs) { + return lhs.offset < rhs.offset; }); + for(size_t i = 0; i < allocations.size() - 1; ++i) + { + TEST(allocations[i+1].offset >= allocations[i].offset + allocations[i].size); + } + + // Check pUserData + { + const AllocData& alloc = allocations.back(); + VmaVirtualAllocationInfo allocInfo = {}; + vmaGetVirtualAllocationInfo(block, alloc.offset, &allocInfo); + TEST((uintptr_t)allocInfo.pUserData == alloc.size * 10); + + vmaSetVirtualAllocationUserData(block, alloc.offset, (void*)(uintptr_t)666); + vmaGetVirtualAllocationInfo(block, alloc.offset, &allocInfo); + TEST((uintptr_t)allocInfo.pUserData == 666); + } + + // Calculate statistics + { + VkDeviceSize allocSizeMin = VK_WHOLE_SIZE, allocSizeMax = 0, allocSizeSum = 0; + std::for_each(allocations.begin(), allocations.end(), [&](const AllocData& a) { + allocSizeMin = std::min(allocSizeMin, a.size); + allocSizeMax = std::max(allocSizeMax, a.size); + allocSizeSum += a.size; + }); + + VmaStatInfo statInfo = {}; + vmaCalculateVirtualBlockStats(block, &statInfo); + TEST(statInfo.allocationCount == allocations.size()); + TEST(statInfo.blockCount == 1); + TEST(statInfo.usedBytes + statInfo.unusedBytes == blockCreateInfo.size); + TEST(statInfo.allocationSizeMax == allocSizeMax); + TEST(statInfo.allocationSizeMin == allocSizeMin); + TEST(statInfo.usedBytes >= allocSizeSum); + } + + // Build JSON dump string + { + char* json = nullptr; + vmaBuildVirtualBlockStatsString(block, &json, VK_TRUE); + int I = 0; // put a breakpoint here to debug + vmaFreeVirtualBlockStatsString(block, json); + } + + // Final cleanup + vmaClearVirtualBlock(block); + vmaDestroyVirtualBlock(block); + } +} + static void TestAllocationVersusResourceSize() { wprintf(L"Test allocation versus resource size\n"); @@ -6696,7 +6834,7 @@ void Test() { //////////////////////////////////////////////////////////////////////////////// // Temporarily insert custom tests here: - TestVirtualBlocks(); + TestVirtualBlocksAlgorithms(); return; }