mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2024-12-26 10:51:01 +00:00
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:
parent
ee79c63d61
commit
70a683e53f
150
src/Tests.cpp
150
src/Tests.cpp
@ -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();
|
||||||
|
@ -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;
|
||||||
{
|
|
||||||
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
|
|
||||||
VMA_ASSERT(pCurrBlock);
|
|
||||||
VmaAllocationRequest currRequest = {};
|
|
||||||
if(pCurrBlock->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)
|
if(m_LinearAlgorithm)
|
||||||
{
|
{
|
||||||
VkResult res = pCurrBlock->Map(m_hAllocator, 1, VMA_NULL);
|
// Use only last block.
|
||||||
if(res != VK_SUCCESS)
|
if(!m_Blocks.empty())
|
||||||
{
|
{
|
||||||
return res;
|
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
|
||||||
}
|
VMA_ASSERT(pCurrBlock);
|
||||||
}
|
VkResult res = AllocateFromBlock(
|
||||||
|
|
||||||
// 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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user