Skip to content

Commit ddcbf8c

Browse files
Fix management of m_HasEmptyBlock by adding VmaBlockVector::UpdateHasEmptyBlock().
Also added TestPool_MinBlockCount().
1 parent 6918555 commit ddcbf8c

File tree

2 files changed

+103
-26
lines changed

2 files changed

+103
-26
lines changed

src/Tests.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,14 +715,17 @@ void AllocInfo::Destroy()
715715
if(m_Image)
716716
{
717717
vkDestroyImage(g_hDevice, m_Image, g_Allocs);
718+
m_Image = VK_NULL_HANDLE;
718719
}
719720
if(m_Buffer)
720721
{
721722
vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
723+
m_Buffer = VK_NULL_HANDLE;
722724
}
723725
if(m_Allocation)
724726
{
725727
vmaFreeMemory(g_hAllocator, m_Allocation);
728+
m_Allocation = VK_NULL_HANDLE;
726729
}
727730
}
728731

@@ -1986,6 +1989,80 @@ static void TestBasics()
19861989
TestInvalidAllocations();
19871990
}
19881991

1992+
static void TestPool_MinBlockCount()
1993+
{
1994+
#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
1995+
return;
1996+
#endif
1997+
1998+
wprintf(L"Test Pool MinBlockCount\n");
1999+
VkResult res;
2000+
2001+
static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
2002+
static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
2003+
2004+
VmaAllocationCreateInfo allocCreateInfo = {};
2005+
allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
2006+
2007+
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
2008+
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2009+
bufCreateInfo.size = ALLOC_SIZE;
2010+
2011+
VmaPoolCreateInfo poolCreateInfo = {};
2012+
poolCreateInfo.blockSize = BLOCK_SIZE;
2013+
poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
2014+
res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
2015+
TEST(res == VK_SUCCESS);
2016+
2017+
VmaPool pool = VK_NULL_HANDLE;
2018+
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
2019+
TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
2020+
2021+
// Check that there are 2 blocks preallocated as requested.
2022+
VmaPoolStats begPoolStats = {};
2023+
vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
2024+
TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
2025+
2026+
// Allocate 5 buffers to create 3 blocks.
2027+
static const uint32_t BUF_COUNT = 5;
2028+
allocCreateInfo.pool = pool;
2029+
std::vector<AllocInfo> allocs(BUF_COUNT);
2030+
for(uint32_t i = 0; i < BUF_COUNT; ++i)
2031+
{
2032+
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
2033+
TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
2034+
}
2035+
2036+
// Check that there are really 3 blocks.
2037+
VmaPoolStats poolStats2 = {};
2038+
vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
2039+
TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
2040+
2041+
// Free two first allocations to make one block empty.
2042+
allocs[0].Destroy();
2043+
allocs[1].Destroy();
2044+
2045+
// Check that there are still 3 blocks due to hysteresis.
2046+
VmaPoolStats poolStats3 = {};
2047+
vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
2048+
TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
2049+
2050+
// Free the last allocation to make second block empty.
2051+
allocs[BUF_COUNT - 1].Destroy();
2052+
2053+
// Check that there are now 2 blocks only.
2054+
VmaPoolStats poolStats4 = {};
2055+
vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
2056+
TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
2057+
2058+
// Cleanup.
2059+
for(size_t i = allocs.size(); i--; )
2060+
{
2061+
allocs[i].Destroy();
2062+
}
2063+
vmaDestroyPool(g_hAllocator, pool);
2064+
}
2065+
19892066
void TestHeapSizeLimit()
19902067
{
19912068
const VkDeviceSize HEAP_SIZE_LIMIT = 200ull * 1024 * 1024; // 200 MB
@@ -5392,6 +5469,7 @@ void Test()
53925469
TestDebugMargin();
53935470
#else
53945471
TestPool_SameSize();
5472+
TestPool_MinBlockCount();
53955473
TestHeapSizeLimit();
53965474
#endif
53975475
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS

src/vk_mem_alloc.h

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6296,11 +6296,11 @@ struct VmaBlockVector
62966296
const uint32_t m_FrameInUseCount;
62976297
const bool m_ExplicitBlockSize;
62986298
const uint32_t m_Algorithm;
6299-
/* There can be at most one allocation that is completely empty - a
6300-
hysteresis to avoid pessimistic case of alternating creation and destruction
6301-
of a VkDeviceMemory. */
6302-
bool m_HasEmptyBlock;
63036299
VMA_RW_MUTEX m_Mutex;
6300+
6301+
/* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
6302+
a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
6303+
bool m_HasEmptyBlock;
63046304
// Incrementally sorted by sumFreeSize, ascending.
63056305
VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
63066306
uint32_t m_NextBlockId;
@@ -6351,6 +6351,8 @@ struct VmaBlockVector
63516351
- updated with new data.
63526352
*/
63536353
void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
6354+
6355+
void UpdateHasEmptyBlock();
63546356
};
63556357

63566358
struct VmaPool_T
@@ -12174,15 +12176,11 @@ VkResult VmaBlockVector::AllocatePage(
1217412176
m_FrameInUseCount,
1217512177
&bestRequest))
1217612178
{
12177-
// We no longer have an empty Allocation.
12178-
if(pBestRequestBlock->m_pMetadata->IsEmpty())
12179-
{
12180-
m_HasEmptyBlock = false;
12181-
}
1218212179
// Allocate from this pBlock.
1218312180
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
1218412181
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
1218512182
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
12183+
UpdateHasEmptyBlock();
1218612184
(*pAllocation)->InitBlockAllocation(
1218712185
pBestRequestBlock,
1218812186
bestRequest.offset,
@@ -12272,11 +12270,7 @@ void VmaBlockVector::Free(
1227212270
pBlockToDelete = pBlock;
1227312271
Remove(pBlock);
1227412272
}
12275-
// We now have first empty block.
12276-
else
12277-
{
12278-
m_HasEmptyBlock = true;
12279-
}
12273+
// else: We now have an empty block - leave it.
1228012274
}
1228112275
// pBlock didn't become empty, but we have another empty block - find and free that one.
1228212276
// (This is optional, heuristics.)
@@ -12287,10 +12281,10 @@ void VmaBlockVector::Free(
1228712281
{
1228812282
pBlockToDelete = pLastBlock;
1228912283
m_Blocks.pop_back();
12290-
m_HasEmptyBlock = false;
1229112284
}
1229212285
}
1229312286

12287+
UpdateHasEmptyBlock();
1229412288
IncrementallySortBlocks();
1229512289
}
1229612290

@@ -12388,15 +12382,10 @@ VkResult VmaBlockVector::AllocateFromBlock(
1238812382
}
1238912383
}
1239012384

12391-
// We no longer have an empty Allocation.
12392-
if(pBlock->m_pMetadata->IsEmpty())
12393-
{
12394-
m_HasEmptyBlock = false;
12395-
}
12396-
1239712385
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
1239812386
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
1239912387
pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
12388+
UpdateHasEmptyBlock();
1240012389
(*pAllocation)->InitBlockAllocation(
1240112390
pBlock,
1240212391
currRequest.offset,
@@ -12650,7 +12639,6 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
1265012639

1265112640
void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
1265212641
{
12653-
m_HasEmptyBlock = false;
1265412642
for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
1265512643
{
1265612644
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
@@ -12668,10 +12656,21 @@ void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationSt
1266812656
pBlock->Destroy(m_hAllocator);
1266912657
vma_delete(m_hAllocator, pBlock);
1267012658
}
12671-
else
12672-
{
12673-
m_HasEmptyBlock = true;
12674-
}
12659+
}
12660+
}
12661+
UpdateHasEmptyBlock();
12662+
}
12663+
12664+
void VmaBlockVector::UpdateHasEmptyBlock()
12665+
{
12666+
m_HasEmptyBlock = false;
12667+
for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
12668+
{
12669+
VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
12670+
if(pBlock->m_pMetadata->IsEmpty())
12671+
{
12672+
m_HasEmptyBlock = true;
12673+
break;
1267512674
}
1267612675
}
1267712676
}

0 commit comments

Comments
 (0)