mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2024-12-29 04:01:05 +00:00
Fix management of m_HasEmptyBlock by adding VmaBlockVector::UpdateHasEmptyBlock().
Also added TestPool_MinBlockCount().
This commit is contained in:
parent
69185555f4
commit
ddcbf8cdba
@ -715,14 +715,17 @@ void AllocInfo::Destroy()
|
||||
if(m_Image)
|
||||
{
|
||||
vkDestroyImage(g_hDevice, m_Image, g_Allocs);
|
||||
m_Image = VK_NULL_HANDLE;
|
||||
}
|
||||
if(m_Buffer)
|
||||
{
|
||||
vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
|
||||
m_Buffer = VK_NULL_HANDLE;
|
||||
}
|
||||
if(m_Allocation)
|
||||
{
|
||||
vmaFreeMemory(g_hAllocator, m_Allocation);
|
||||
m_Allocation = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1986,6 +1989,80 @@ static void TestBasics()
|
||||
TestInvalidAllocations();
|
||||
}
|
||||
|
||||
static void TestPool_MinBlockCount()
|
||||
{
|
||||
#if defined(VMA_DEBUG_MARGIN) && VMA_DEBUG_MARGIN > 0
|
||||
return;
|
||||
#endif
|
||||
|
||||
wprintf(L"Test Pool MinBlockCount\n");
|
||||
VkResult res;
|
||||
|
||||
static const VkDeviceSize ALLOC_SIZE = 512ull * 1024;
|
||||
static const VkDeviceSize BLOCK_SIZE = ALLOC_SIZE * 2; // Each block can fit 2 allocations.
|
||||
|
||||
VmaAllocationCreateInfo allocCreateInfo = {};
|
||||
allocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_COPY;
|
||||
|
||||
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
||||
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
bufCreateInfo.size = ALLOC_SIZE;
|
||||
|
||||
VmaPoolCreateInfo poolCreateInfo = {};
|
||||
poolCreateInfo.blockSize = BLOCK_SIZE;
|
||||
poolCreateInfo.minBlockCount = 2; // At least 2 blocks always present.
|
||||
res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &poolCreateInfo.memoryTypeIndex);
|
||||
TEST(res == VK_SUCCESS);
|
||||
|
||||
VmaPool pool = VK_NULL_HANDLE;
|
||||
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
|
||||
TEST(res == VK_SUCCESS && pool != VK_NULL_HANDLE);
|
||||
|
||||
// Check that there are 2 blocks preallocated as requested.
|
||||
VmaPoolStats begPoolStats = {};
|
||||
vmaGetPoolStats(g_hAllocator, pool, &begPoolStats);
|
||||
TEST(begPoolStats.blockCount == 2 && begPoolStats.allocationCount == 0 && begPoolStats.size == BLOCK_SIZE * 2);
|
||||
|
||||
// Allocate 5 buffers to create 3 blocks.
|
||||
static const uint32_t BUF_COUNT = 5;
|
||||
allocCreateInfo.pool = pool;
|
||||
std::vector<AllocInfo> allocs(BUF_COUNT);
|
||||
for(uint32_t i = 0; i < BUF_COUNT; ++i)
|
||||
{
|
||||
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo, &allocs[i].m_Buffer, &allocs[i].m_Allocation, nullptr);
|
||||
TEST(res == VK_SUCCESS && allocs[i].m_Buffer != VK_NULL_HANDLE && allocs[i].m_Allocation != VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
// Check that there are really 3 blocks.
|
||||
VmaPoolStats poolStats2 = {};
|
||||
vmaGetPoolStats(g_hAllocator, pool, &poolStats2);
|
||||
TEST(poolStats2.blockCount == 3 && poolStats2.allocationCount == BUF_COUNT && poolStats2.size == BLOCK_SIZE * 3);
|
||||
|
||||
// Free two first allocations to make one block empty.
|
||||
allocs[0].Destroy();
|
||||
allocs[1].Destroy();
|
||||
|
||||
// Check that there are still 3 blocks due to hysteresis.
|
||||
VmaPoolStats poolStats3 = {};
|
||||
vmaGetPoolStats(g_hAllocator, pool, &poolStats3);
|
||||
TEST(poolStats3.blockCount == 3 && poolStats3.allocationCount == BUF_COUNT - 2 && poolStats2.size == BLOCK_SIZE * 3);
|
||||
|
||||
// Free the last allocation to make second block empty.
|
||||
allocs[BUF_COUNT - 1].Destroy();
|
||||
|
||||
// Check that there are now 2 blocks only.
|
||||
VmaPoolStats poolStats4 = {};
|
||||
vmaGetPoolStats(g_hAllocator, pool, &poolStats4);
|
||||
TEST(poolStats4.blockCount == 2 && poolStats4.allocationCount == BUF_COUNT - 3 && poolStats4.size == BLOCK_SIZE * 2);
|
||||
|
||||
// Cleanup.
|
||||
for(size_t i = allocs.size(); i--; )
|
||||
{
|
||||
allocs[i].Destroy();
|
||||
}
|
||||
vmaDestroyPool(g_hAllocator, pool);
|
||||
}
|
||||
|
||||
void TestHeapSizeLimit()
|
||||
{
|
||||
const VkDeviceSize HEAP_SIZE_LIMIT = 200ull * 1024 * 1024; // 200 MB
|
||||
@ -5392,6 +5469,7 @@ void Test()
|
||||
TestDebugMargin();
|
||||
#else
|
||||
TestPool_SameSize();
|
||||
TestPool_MinBlockCount();
|
||||
TestHeapSizeLimit();
|
||||
#endif
|
||||
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
|
||||
|
@ -6296,11 +6296,11 @@ private:
|
||||
const uint32_t m_FrameInUseCount;
|
||||
const bool m_ExplicitBlockSize;
|
||||
const uint32_t m_Algorithm;
|
||||
/* There can be at most one allocation that is completely empty - a
|
||||
hysteresis to avoid pessimistic case of alternating creation and destruction
|
||||
of a VkDeviceMemory. */
|
||||
bool m_HasEmptyBlock;
|
||||
VMA_RW_MUTEX m_Mutex;
|
||||
|
||||
/* There can be at most one allocation that is completely empty (except when minBlockCount > 0) -
|
||||
a hysteresis to avoid pessimistic case of alternating creation and destruction of a VkDeviceMemory. */
|
||||
bool m_HasEmptyBlock;
|
||||
// Incrementally sorted by sumFreeSize, ascending.
|
||||
VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
|
||||
uint32_t m_NextBlockId;
|
||||
@ -6351,6 +6351,8 @@ private:
|
||||
- updated with new data.
|
||||
*/
|
||||
void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
|
||||
|
||||
void UpdateHasEmptyBlock();
|
||||
};
|
||||
|
||||
struct VmaPool_T
|
||||
@ -12174,15 +12176,11 @@ VkResult VmaBlockVector::AllocatePage(
|
||||
m_FrameInUseCount,
|
||||
&bestRequest))
|
||||
{
|
||||
// We no longer have an empty Allocation.
|
||||
if(pBestRequestBlock->m_pMetadata->IsEmpty())
|
||||
{
|
||||
m_HasEmptyBlock = false;
|
||||
}
|
||||
// Allocate from this pBlock.
|
||||
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
|
||||
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
||||
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
|
||||
UpdateHasEmptyBlock();
|
||||
(*pAllocation)->InitBlockAllocation(
|
||||
pBestRequestBlock,
|
||||
bestRequest.offset,
|
||||
@ -12272,11 +12270,7 @@ void VmaBlockVector::Free(
|
||||
pBlockToDelete = pBlock;
|
||||
Remove(pBlock);
|
||||
}
|
||||
// We now have first empty block.
|
||||
else
|
||||
{
|
||||
m_HasEmptyBlock = true;
|
||||
}
|
||||
// else: We now have an empty block - leave it.
|
||||
}
|
||||
// pBlock didn't become empty, but we have another empty block - find and free that one.
|
||||
// (This is optional, heuristics.)
|
||||
@ -12287,10 +12281,10 @@ void VmaBlockVector::Free(
|
||||
{
|
||||
pBlockToDelete = pLastBlock;
|
||||
m_Blocks.pop_back();
|
||||
m_HasEmptyBlock = false;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateHasEmptyBlock();
|
||||
IncrementallySortBlocks();
|
||||
}
|
||||
|
||||
@ -12388,15 +12382,10 @@ VkResult VmaBlockVector::AllocateFromBlock(
|
||||
}
|
||||
}
|
||||
|
||||
// We no longer have an empty Allocation.
|
||||
if(pBlock->m_pMetadata->IsEmpty())
|
||||
{
|
||||
m_HasEmptyBlock = false;
|
||||
}
|
||||
|
||||
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
|
||||
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
||||
pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
|
||||
UpdateHasEmptyBlock();
|
||||
(*pAllocation)->InitBlockAllocation(
|
||||
pBlock,
|
||||
currRequest.offset,
|
||||
@ -12650,7 +12639,6 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
|
||||
|
||||
void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
|
||||
{
|
||||
m_HasEmptyBlock = false;
|
||||
for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
|
||||
{
|
||||
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
|
||||
@ -12668,10 +12656,21 @@ void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationSt
|
||||
pBlock->Destroy(m_hAllocator);
|
||||
vma_delete(m_hAllocator, pBlock);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_HasEmptyBlock = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
UpdateHasEmptyBlock();
|
||||
}
|
||||
|
||||
void VmaBlockVector::UpdateHasEmptyBlock()
|
||||
{
|
||||
m_HasEmptyBlock = false;
|
||||
for(size_t index = 0, count = m_Blocks.size(); index < count; ++index)
|
||||
{
|
||||
VmaDeviceMemoryBlock* const pBlock = m_Blocks[index];
|
||||
if(pBlock->m_pMetadata->IsEmpty())
|
||||
{
|
||||
m_HasEmptyBlock = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user