Added support for multiple Vulkan memory blocks in custom pools with VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. Works with free-at-once and stack, doesn't work with double stack or ring buffer.

Added new structure members VmaPoolStats::blockCount.
This commit is contained in:
Adam Sawicki 2018-08-24 15:36:32 +02:00
parent ee79c63d61
commit 70a683e53f
2 changed files with 324 additions and 120 deletions

View File

@ -1839,14 +1839,139 @@ static void TestLinearAllocator()
bufInfo.clear(); bufInfo.clear();
} }
// Try to create pool with maxBlockCount higher than 1. It should fail. vmaDestroyPool(g_hAllocator, pool);
{ }
VmaPoolCreateInfo altPoolCreateInfo = poolCreateInfo;
altPoolCreateInfo.maxBlockCount = 2;
VmaPool altPool = nullptr; static void TestLinearAllocatorMultiBlock()
res = vmaCreatePool(g_hAllocator, &altPoolCreateInfo, &altPool); {
assert(res != VK_SUCCESS); wprintf(L"Test linear allocator multi block\n");
RandomNumberGenerator rand{345673};
VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
sampleBufCreateInfo.size = 1024 * 1024;
sampleBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo sampleAllocCreateInfo = {};
sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
VmaPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.flags = VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT;
VkResult res = vmaFindMemoryTypeIndexForBufferInfo(g_hAllocator, &sampleBufCreateInfo, &sampleAllocCreateInfo, &poolCreateInfo.memoryTypeIndex);
assert(res == VK_SUCCESS);
VmaPool pool = nullptr;
res = vmaCreatePool(g_hAllocator, &poolCreateInfo, &pool);
assert(res == VK_SUCCESS);
VkBufferCreateInfo bufCreateInfo = sampleBufCreateInfo;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.pool = pool;
std::vector<BufferInfo> bufInfo;
VmaAllocationInfo allocInfo;
// Test one-time free.
{
// Allocate buffers until we move to a second block.
VkDeviceMemory lastMem = VK_NULL_HANDLE;
for(uint32_t i = 0; ; ++i)
{
BufferInfo newBufInfo;
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
&newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
assert(res == VK_SUCCESS);
bufInfo.push_back(newBufInfo);
if(lastMem && allocInfo.deviceMemory != lastMem)
{
break;
}
lastMem = allocInfo.deviceMemory;
}
assert(bufInfo.size() > 2);
// Make sure that pool has now two blocks.
VmaPoolStats poolStats = {};
vmaGetPoolStats(g_hAllocator, pool, &poolStats);
assert(poolStats.blockCount == 2);
// Destroy all the buffers in random order.
while(!bufInfo.empty())
{
const size_t indexToDestroy = rand.Generate() % bufInfo.size();
const BufferInfo& currBufInfo = bufInfo[indexToDestroy];
vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
bufInfo.erase(bufInfo.begin() + indexToDestroy);
}
// Make sure that pool has now at most one block.
vmaGetPoolStats(g_hAllocator, pool, &poolStats);
assert(poolStats.blockCount <= 1);
}
// Test stack.
{
// Allocate buffers until we move to a second block.
VkDeviceMemory lastMem = VK_NULL_HANDLE;
for(uint32_t i = 0; ; ++i)
{
BufferInfo newBufInfo;
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
&newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
assert(res == VK_SUCCESS);
bufInfo.push_back(newBufInfo);
if(lastMem && allocInfo.deviceMemory != lastMem)
{
break;
}
lastMem = allocInfo.deviceMemory;
}
assert(bufInfo.size() > 2);
// Add few more buffers.
for(uint32_t i = 0; i < 5; ++i)
{
BufferInfo newBufInfo;
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
&newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
assert(res == VK_SUCCESS);
bufInfo.push_back(newBufInfo);
}
// Make sure that pool has now two blocks.
VmaPoolStats poolStats = {};
vmaGetPoolStats(g_hAllocator, pool, &poolStats);
assert(poolStats.blockCount == 2);
// Delete half of buffers, LIFO.
for(size_t i = 0, countToDelete = bufInfo.size() / 2; i < countToDelete; ++i)
{
const BufferInfo& currBufInfo = bufInfo.back();
vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
bufInfo.pop_back();
}
// Add one more buffer.
BufferInfo newBufInfo;
res = vmaCreateBuffer(g_hAllocator, &bufCreateInfo, &allocCreateInfo,
&newBufInfo.Buffer, &newBufInfo.Allocation, &allocInfo);
assert(res == VK_SUCCESS);
bufInfo.push_back(newBufInfo);
// Make sure that pool has now one block.
vmaGetPoolStats(g_hAllocator, pool, &poolStats);
assert(poolStats.blockCount == 1);
// Delete all the remaining buffers, LIFO.
while(!bufInfo.empty())
{
const BufferInfo& currBufInfo = bufInfo.back();
vmaDestroyBuffer(g_hAllocator, currBufInfo.Buffer, currBufInfo.Allocation);
bufInfo.pop_back();
}
} }
vmaDestroyPool(g_hAllocator, pool); vmaDestroyPool(g_hAllocator, pool);
@ -3841,6 +3966,16 @@ void Test()
{ {
wprintf(L"TESTING:\n"); wprintf(L"TESTING:\n");
if(false)
{
// # Temporarily insert custom tests here
TestLinearAllocator();
ManuallyTestLinearAllocator();
TestLinearAllocatorMultiBlock();
BenchmarkLinearAllocator();
return;
}
// # Simple tests // # Simple tests
TestBasics(); TestBasics();
@ -3857,6 +3992,7 @@ void Test()
TestMappingMultithreaded(); TestMappingMultithreaded();
TestLinearAllocator(); TestLinearAllocator();
ManuallyTestLinearAllocator(); ManuallyTestLinearAllocator();
TestLinearAllocatorMultiBlock();
BenchmarkLinearAllocator(); BenchmarkLinearAllocator();
TestDefragmentationSimple(); TestDefragmentationSimple();
TestDefragmentationFull(); TestDefragmentationFull();

View File

@ -592,9 +592,6 @@ less memory consumed by metadata.
With this one flag, you can create a custom pool that can be used in many ways: With this one flag, you can create a custom pool that can be used in many ways:
free-at-once, stack, double stack, and ring buffer. See below for details. free-at-once, stack, double stack, and ring buffer. See below for details.
Pools with linear algorithm must have only one memory block -
VmaPoolCreateInfo::maxBlockCount must be 1.
\subsection linear_algorithm_free_at_once Free-at-once \subsection linear_algorithm_free_at_once Free-at-once
In a pool that uses linear algorithm, you still need to free all the allocations In a pool that uses linear algorithm, you still need to free all the allocations
@ -607,6 +604,9 @@ to release all at once.
![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) ![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png)
This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
value that allows multiple memory blocks.
\subsection linear_algorithm_stack Stack \subsection linear_algorithm_stack Stack
When you free an allocation that was created last, its space can be reused. When you free an allocation that was created last, its space can be reused.
@ -615,6 +615,9 @@ creation (LIFO - Last In First Out), you can achieve behavior of a stack.
![Stack](../gfx/Linear_allocator_4_stack.png) ![Stack](../gfx/Linear_allocator_4_stack.png)
This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount
value that allows multiple memory blocks.
\subsection linear_algorithm_double_stack Double stack \subsection linear_algorithm_double_stack Double stack
The space reserved by a custom pool with linear algorithm may be used by two The space reserved by a custom pool with linear algorithm may be used by two
@ -626,12 +629,15 @@ stacks:
To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
to VmaAllocationCreateInfo::flags. to VmaAllocationCreateInfo::flags.
![Double stack](../gfx/Linear_allocator_7_double_stack.png)
Double stack is available only in pools with one memory block -
VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
When the two stacks' ends meet so there is not enough space between them for a When the two stacks' ends meet so there is not enough space between them for a
new allocation, such allocation fails with usual new allocation, such allocation fails with usual
`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. `VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
![Double stack](../gfx/Linear_allocator_7_double_stack.png)
\subsection linear_algorithm_ring_buffer Ring buffer \subsection linear_algorithm_ring_buffer Ring buffer
When you free some allocations from the beginning and there is not enough free space When you free some allocations from the beginning and there is not enough free space
@ -649,6 +655,9 @@ succeeds.
![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png) ![Ring buffer with lost allocations](../gfx/Linear_allocator_6_ring_buffer_lost.png)
Ring buffer is available only in pools with one memory block -
VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined.
\page defragmentation Defragmentation \page defragmentation Defragmentation
@ -1968,7 +1977,6 @@ typedef struct VmaPoolCreateInfo {
/** \brief Maximum number of blocks that can be allocated in this pool. Optional. /** \brief Maximum number of blocks that can be allocated in this pool. Optional.
Set to 0 to use default, which is `SIZE_MAX`, which means no limit. Set to 0 to use default, which is `SIZE_MAX`, which means no limit.
When #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT is used, default is 1.
Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated
throughout whole lifetime of this pool. throughout whole lifetime of this pool.
@ -2005,13 +2013,16 @@ typedef struct VmaPoolStats {
/** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation. /** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
*/ */
size_t unusedRangeCount; size_t unusedRangeCount;
/** \brief Size of the largest continuous free memory region. /** \brief Size of the largest continuous free memory region available for new allocation.
Making a new allocation of that size is not guaranteed to succeed because of Making a new allocation of that size is not guaranteed to succeed because of
possible additional margin required to respect alignment and buffer/image possible additional margin required to respect alignment and buffer/image
granularity. granularity.
*/ */
VkDeviceSize unusedRangeSizeMax; VkDeviceSize unusedRangeSizeMax;
/** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
*/
size_t blockCount;
} VmaPoolStats; } VmaPoolStats;
/** \brief Allocates Vulkan device memory and creates #VmaPool object. /** \brief Allocates Vulkan device memory and creates #VmaPool object.
@ -4506,6 +4517,7 @@ public:
virtual bool IsEmpty() const = 0; virtual bool IsEmpty() const = 0;
virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0; virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
// Shouldn't modify blockCount.
virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0; virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
#if VMA_STATS_STRING_ENABLED #if VMA_STATS_STRING_ENABLED
@ -5014,6 +5026,18 @@ private:
// after this call. // after this call.
void IncrementallySortBlocks(); void IncrementallySortBlocks();
// To be used only without CAN_MAKE_OTHER_LOST flag.
VkResult AllocateFromBlock(
VmaDeviceMemoryBlock* pBlock,
VmaPool hCurrentPool,
uint32_t currentFrameIndex,
VkDeviceSize size,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation);
VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
}; };
@ -9374,15 +9398,18 @@ VkResult VmaBlockVector::CreateMinBlocks()
void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats) void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
{ {
VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
const size_t blockCount = m_Blocks.size();
pStats->size = 0; pStats->size = 0;
pStats->unusedSize = 0; pStats->unusedSize = 0;
pStats->allocationCount = 0; pStats->allocationCount = 0;
pStats->unusedRangeCount = 0; pStats->unusedRangeCount = 0;
pStats->unusedRangeSizeMax = 0; pStats->unusedRangeSizeMax = 0;
pStats->blockCount = blockCount;
VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex); for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
{ {
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
VMA_ASSERT(pBlock); VMA_ASSERT(pBlock);
@ -9411,15 +9438,23 @@ VkResult VmaBlockVector::Allocate(
VmaAllocation* pAllocation) VmaAllocation* pAllocation)
{ {
const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0; bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; const bool mapped = (createInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; const bool isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
const bool canCreateNewBlock = const bool canCreateNewBlock =
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
(m_Blocks.size() < m_MaxBlockCount); (m_Blocks.size() < m_MaxBlockCount);
// Upper address can only be used with linear allocator. // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
if(isUpperAddress && !m_LinearAlgorithm) // Which in turn is available only when maxBlockCount = 1.
if(m_LinearAlgorithm && m_MaxBlockCount > 1)
{
canMakeOtherLost = false;
}
// Upper address can only be used with linear allocator and within single memory block.
if(isUpperAddress &&
(!m_LinearAlgorithm || m_MaxBlockCount > 1))
{ {
return VK_ERROR_FEATURE_NOT_PRESENT; return VK_ERROR_FEATURE_NOT_PRESENT;
} }
@ -9440,65 +9475,55 @@ VkResult VmaBlockVector::Allocate(
if(!canMakeOtherLost || canCreateNewBlock) if(!canMakeOtherLost || canCreateNewBlock)
{ {
// 1. Search existing allocations. Try to allocate without making other allocations lost. // 1. Search existing allocations. Try to allocate without making other allocations lost.
// Forward order in m_Blocks - prefer blocks with smallest amount of free space. VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex ) allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
if(m_LinearAlgorithm)
{ {
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; // Use only last block.
VMA_ASSERT(pCurrBlock); if(!m_Blocks.empty())
VmaAllocationRequest currRequest = {};
if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
currentFrameIndex,
m_FrameInUseCount,
m_BufferImageGranularity,
size,
alignment,
isUpperAddress,
suballocType,
false, // canMakeOtherLost
&currRequest))
{ {
// Allocate from pCurrBlock. VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
VMA_ASSERT(currRequest.itemsToMakeLostCount == 0); VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(
if(mapped)
{
VkResult res = pCurrBlock->Map(m_hAllocator, 1, VMA_NULL);
if(res != VK_SUCCESS)
{
return res;
}
}
// We no longer have an empty Allocation.
if(pCurrBlock->m_pMetadata->IsEmpty())
{
m_HasEmptyBlock = false;
}
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
pCurrBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
(*pAllocation)->InitBlockAllocation(
hCurrentPool,
pCurrBlock, pCurrBlock,
currRequest.offset, hCurrentPool,
alignment, currentFrameIndex,
size, size,
alignment,
allocFlagsCopy,
createInfo.pUserData,
suballocType, suballocType,
mapped, pAllocation);
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0); if(res == VK_SUCCESS)
VMA_HEAVY_ASSERT(pCurrBlock->Validate());
VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
(*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{ {
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); VMA_DEBUG_LOG(" Returned from last block #%u", (uint32_t)(m_Blocks.size() - 1));
return VK_SUCCESS;
} }
if(IsCorruptionDetectionEnabled()) }
}
else
{
// Forward order in m_Blocks - prefer blocks with smallest amount of free space.
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
{
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(
pCurrBlock,
hCurrentPool,
currentFrameIndex,
size,
alignment,
allocFlagsCopy,
createInfo.pUserData,
suballocType,
pAllocation);
if(res == VK_SUCCESS)
{ {
VkResult res = pCurrBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size); VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); return VK_SUCCESS;
} }
return VK_SUCCESS;
} }
} }
@ -9555,56 +9580,24 @@ VkResult VmaBlockVector::Allocate(
VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
if(mapped) res = AllocateFromBlock(
{ pBlock,
res = pBlock->Map(m_hAllocator, 1, VMA_NULL); hCurrentPool,
if(res != VK_SUCCESS)
{
return res;
}
}
// Allocate from pBlock. Because it is empty, dstAllocRequest can be trivially filled.
VmaAllocationRequest allocRequest;
if(pBlock->m_pMetadata->CreateAllocationRequest(
currentFrameIndex, currentFrameIndex,
m_FrameInUseCount,
m_BufferImageGranularity,
size, size,
alignment, alignment,
isUpperAddress, allocFlagsCopy,
createInfo.pUserData,
suballocType, suballocType,
false, // canMakeOtherLost pAllocation);
&allocRequest)) if(res == VK_SUCCESS)
{ {
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString); VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
pBlock->m_pMetadata->Alloc(allocRequest, suballocType, size, isUpperAddress, *pAllocation);
(*pAllocation)->InitBlockAllocation(
hCurrentPool,
pBlock,
allocRequest.offset,
alignment,
size,
suballocType,
mapped,
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
VMA_HEAVY_ASSERT(pBlock->Validate());
VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
(*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
}
if(IsCorruptionDetectionEnabled())
{
res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, allocRequest.offset, size);
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
}
return VK_SUCCESS; return VK_SUCCESS;
} }
else else
{ {
// Allocation from empty block failed, possibly due to VMA_DEBUG_MARGIN or alignment. // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
return VK_ERROR_OUT_OF_DEVICE_MEMORY; return VK_ERROR_OUT_OF_DEVICE_MEMORY;
} }
} }
@ -9819,17 +9812,93 @@ void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock)
void VmaBlockVector::IncrementallySortBlocks() void VmaBlockVector::IncrementallySortBlocks()
{ {
// Bubble sort only until first swap. if(!m_LinearAlgorithm)
for(size_t i = 1; i < m_Blocks.size(); ++i)
{ {
if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) // Bubble sort only until first swap.
for(size_t i = 1; i < m_Blocks.size(); ++i)
{ {
VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
return; {
VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]);
return;
}
} }
} }
} }
VkResult VmaBlockVector::AllocateFromBlock(
VmaDeviceMemoryBlock* pBlock,
VmaPool hCurrentPool,
uint32_t currentFrameIndex,
VkDeviceSize size,
VkDeviceSize alignment,
VmaAllocationCreateFlags allocFlags,
void* pUserData,
VmaSuballocationType suballocType,
VmaAllocation* pAllocation)
{
VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0;
const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
VmaAllocationRequest currRequest = {};
if(pBlock->m_pMetadata->CreateAllocationRequest(
currentFrameIndex,
m_FrameInUseCount,
m_BufferImageGranularity,
size,
alignment,
isUpperAddress,
suballocType,
false, // canMakeOtherLost
&currRequest))
{
// Allocate from pCurrBlock.
VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
if(mapped)
{
VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
if(res != VK_SUCCESS)
{
return res;
}
}
// We no longer have an empty Allocation.
if(pBlock->m_pMetadata->IsEmpty())
{
m_HasEmptyBlock = false;
}
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
pBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
(*pAllocation)->InitBlockAllocation(
hCurrentPool,
pBlock,
currRequest.offset,
alignment,
size,
suballocType,
mapped,
(allocFlags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
VMA_HEAVY_ASSERT(pBlock->Validate());
(*pAllocation)->SetUserData(m_hAllocator, pUserData);
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
{
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
}
if(IsCorruptionDetectionEnabled())
{
VkResult res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
}
return VK_SUCCESS;
}
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
}
VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex)
{ {
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
@ -11812,10 +11881,9 @@ VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPoo
if(newCreateInfo.maxBlockCount == 0) if(newCreateInfo.maxBlockCount == 0)
{ {
newCreateInfo.maxBlockCount = isLinearAlgorithm ? 1 : SIZE_MAX; newCreateInfo.maxBlockCount = SIZE_MAX;
} }
if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount || if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
isLinearAlgorithm && newCreateInfo.maxBlockCount > 1)
{ {
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }