mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git
synced 2024-11-26 16:34:35 +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)
|
if(m_Image)
|
||||||
{
|
{
|
||||||
vkDestroyImage(g_hDevice, m_Image, g_Allocs);
|
vkDestroyImage(g_hDevice, m_Image, g_Allocs);
|
||||||
|
m_Image = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if(m_Buffer)
|
if(m_Buffer)
|
||||||
{
|
{
|
||||||
vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
|
vkDestroyBuffer(g_hDevice, m_Buffer, g_Allocs);
|
||||||
|
m_Buffer = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
if(m_Allocation)
|
if(m_Allocation)
|
||||||
{
|
{
|
||||||
vmaFreeMemory(g_hAllocator, m_Allocation);
|
vmaFreeMemory(g_hAllocator, m_Allocation);
|
||||||
|
m_Allocation = VK_NULL_HANDLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1986,6 +1989,80 @@ static void TestBasics()
|
|||||||
TestInvalidAllocations();
|
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()
|
void TestHeapSizeLimit()
|
||||||
{
|
{
|
||||||
const VkDeviceSize HEAP_SIZE_LIMIT = 200ull * 1024 * 1024; // 200 MB
|
const VkDeviceSize HEAP_SIZE_LIMIT = 200ull * 1024 * 1024; // 200 MB
|
||||||
@ -5392,6 +5469,7 @@ void Test()
|
|||||||
TestDebugMargin();
|
TestDebugMargin();
|
||||||
#else
|
#else
|
||||||
TestPool_SameSize();
|
TestPool_SameSize();
|
||||||
|
TestPool_MinBlockCount();
|
||||||
TestHeapSizeLimit();
|
TestHeapSizeLimit();
|
||||||
#endif
|
#endif
|
||||||
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
|
#if VMA_DEBUG_INITIALIZE_ALLOCATIONS
|
||||||
|
@ -6296,11 +6296,11 @@ private:
|
|||||||
const uint32_t m_FrameInUseCount;
|
const uint32_t m_FrameInUseCount;
|
||||||
const bool m_ExplicitBlockSize;
|
const bool m_ExplicitBlockSize;
|
||||||
const uint32_t m_Algorithm;
|
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;
|
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.
|
// Incrementally sorted by sumFreeSize, ascending.
|
||||||
VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
|
VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
|
||||||
uint32_t m_NextBlockId;
|
uint32_t m_NextBlockId;
|
||||||
@ -6351,6 +6351,8 @@ private:
|
|||||||
- updated with new data.
|
- updated with new data.
|
||||||
*/
|
*/
|
||||||
void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
|
void FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats);
|
||||||
|
|
||||||
|
void UpdateHasEmptyBlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VmaPool_T
|
struct VmaPool_T
|
||||||
@ -12174,15 +12176,11 @@ VkResult VmaBlockVector::AllocatePage(
|
|||||||
m_FrameInUseCount,
|
m_FrameInUseCount,
|
||||||
&bestRequest))
|
&bestRequest))
|
||||||
{
|
{
|
||||||
// We no longer have an empty Allocation.
|
|
||||||
if(pBestRequestBlock->m_pMetadata->IsEmpty())
|
|
||||||
{
|
|
||||||
m_HasEmptyBlock = false;
|
|
||||||
}
|
|
||||||
// Allocate from this pBlock.
|
// Allocate from this pBlock.
|
||||||
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
|
*pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate();
|
||||||
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
||||||
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
|
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, *pAllocation);
|
||||||
|
UpdateHasEmptyBlock();
|
||||||
(*pAllocation)->InitBlockAllocation(
|
(*pAllocation)->InitBlockAllocation(
|
||||||
pBestRequestBlock,
|
pBestRequestBlock,
|
||||||
bestRequest.offset,
|
bestRequest.offset,
|
||||||
@ -12272,11 +12270,7 @@ void VmaBlockVector::Free(
|
|||||||
pBlockToDelete = pBlock;
|
pBlockToDelete = pBlock;
|
||||||
Remove(pBlock);
|
Remove(pBlock);
|
||||||
}
|
}
|
||||||
// We now have first empty block.
|
// else: We now have an empty block - leave it.
|
||||||
else
|
|
||||||
{
|
|
||||||
m_HasEmptyBlock = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// pBlock didn't become empty, but we have another empty block - find and free that one.
|
// pBlock didn't become empty, but we have another empty block - find and free that one.
|
||||||
// (This is optional, heuristics.)
|
// (This is optional, heuristics.)
|
||||||
@ -12287,10 +12281,10 @@ void VmaBlockVector::Free(
|
|||||||
{
|
{
|
||||||
pBlockToDelete = pLastBlock;
|
pBlockToDelete = pLastBlock;
|
||||||
m_Blocks.pop_back();
|
m_Blocks.pop_back();
|
||||||
m_HasEmptyBlock = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateHasEmptyBlock();
|
||||||
IncrementallySortBlocks();
|
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 = m_hAllocator->m_AllocationObjectAllocator.Allocate();
|
||||||
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
(*pAllocation)->Ctor(currentFrameIndex, isUserDataString);
|
||||||
pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
|
pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, *pAllocation);
|
||||||
|
UpdateHasEmptyBlock();
|
||||||
(*pAllocation)->InitBlockAllocation(
|
(*pAllocation)->InitBlockAllocation(
|
||||||
pBlock,
|
pBlock,
|
||||||
currRequest.offset,
|
currRequest.offset,
|
||||||
@ -12650,7 +12639,6 @@ void VmaBlockVector::ApplyDefragmentationMovesGpu(
|
|||||||
|
|
||||||
void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
|
void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationStats)
|
||||||
{
|
{
|
||||||
m_HasEmptyBlock = false;
|
|
||||||
for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
|
for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
|
||||||
{
|
{
|
||||||
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
|
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
|
||||||
@ -12668,10 +12656,21 @@ void VmaBlockVector::FreeEmptyBlocks(VmaDefragmentationStats* pDefragmentationSt
|
|||||||
pBlock->Destroy(m_hAllocator);
|
pBlock->Destroy(m_hAllocator);
|
||||||
vma_delete(m_hAllocator, pBlock);
|
vma_delete(m_hAllocator, pBlock);
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
}
|
||||||
|
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;
|
m_HasEmptyBlock = true;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user