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.
diff --git a/src/Tests.cpp b/src/Tests.cpp
index 36e86bc..842fba2 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -1839,14 +1839,139 @@
bufInfo.clear();
}
- // Try to create pool with maxBlockCount higher than 1. It should fail.
- {
- VmaPoolCreateInfo altPoolCreateInfo = poolCreateInfo;
- altPoolCreateInfo.maxBlockCount = 2;
+ vmaDestroyPool(g_hAllocator, pool);
+}
- VmaPool altPool = nullptr;
- res = vmaCreatePool(g_hAllocator, &altPoolCreateInfo, &altPool);
- assert(res != VK_SUCCESS);
+static void TestLinearAllocatorMultiBlock()
+{
+ 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);
@@ -3841,6 +3966,16 @@
{
wprintf(L"TESTING:\n");
+ if(false)
+ {
+ // # Temporarily insert custom tests here
+TestLinearAllocator();
+ManuallyTestLinearAllocator();
+TestLinearAllocatorMultiBlock();
+BenchmarkLinearAllocator();
+ return;
+ }
+
// # Simple tests
TestBasics();
@@ -3857,6 +3992,7 @@
TestMappingMultithreaded();
TestLinearAllocator();
ManuallyTestLinearAllocator();
+ TestLinearAllocatorMultiBlock();
BenchmarkLinearAllocator();
TestDefragmentationSimple();
TestDefragmentationFull();
diff --git a/src/vk_mem_alloc.h b/src/vk_mem_alloc.h
index 2e5f400..7dd8ff2 100644
--- a/src/vk_mem_alloc.h
+++ b/src/vk_mem_alloc.h
@@ -592,9 +592,6 @@
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.
-Pools with linear algorithm must have only one memory block -
-VmaPoolCreateInfo::maxBlockCount must be 1.
-
\subsection linear_algorithm_free_at_once Free-at-once
In a pool that uses linear algorithm, you still need to free all the allocations
@@ -607,6 +604,9 @@
![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
When you free an allocation that was created last, its space can be reused.
@@ -615,6 +615,9 @@
![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
The space reserved by a custom pool with linear algorithm may be used by two
@@ -626,12 +629,15 @@
To make allocation from upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT
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
new allocation, such allocation fails with usual
`VK_ERROR_OUT_OF_DEVICE_MEMORY` error.
-![Double stack](../gfx/Linear_allocator_7_double_stack.png)
-
\subsection linear_algorithm_ring_buffer Ring buffer
When you free some allocations from the beginning and there is not enough free space
@@ -649,6 +655,9 @@
![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
@@ -1968,7 +1977,6 @@
/** \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.
- 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
throughout whole lifetime of this pool.
@@ -2005,13 +2013,16 @@
/** \brief Number of continuous memory ranges in the pool not used by any #VmaAllocation.
*/
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
possible additional margin required to respect alignment and buffer/image
granularity.
*/
VkDeviceSize unusedRangeSizeMax;
+ /** \brief Number of `VkDeviceMemory` blocks allocated for this pool.
+ */
+ size_t blockCount;
} VmaPoolStats;
/** \brief Allocates Vulkan device memory and creates #VmaPool object.
@@ -4506,6 +4517,7 @@
virtual bool IsEmpty() const = 0;
virtual void CalcAllocationStatInfo(VmaStatInfo& outInfo) const = 0;
+ // Shouldn't modify blockCount.
virtual void AddPoolStats(VmaPoolStats& inoutStats) const = 0;
#if VMA_STATS_STRING_ENABLED
@@ -5014,6 +5026,18 @@
// after this call.
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);
};
@@ -9374,15 +9398,18 @@
void VmaBlockVector::GetPoolStats(VmaPoolStats* pStats)
{
+ VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
+
+ const size_t blockCount = m_Blocks.size();
+
pStats->size = 0;
pStats->unusedSize = 0;
pStats->allocationCount = 0;
pStats->unusedRangeCount = 0;
pStats->unusedRangeSizeMax = 0;
+ pStats->blockCount = blockCount;
- VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
-
- for(uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex)
+ for(uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex)
{
const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex];
VMA_ASSERT(pBlock);
@@ -9411,15 +9438,23 @@
VmaAllocation* pAllocation)
{
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 isUserDataString = (createInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0;
const bool canCreateNewBlock =
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
(m_Blocks.size() < m_MaxBlockCount);
- // Upper address can only be used with linear allocator.
- if(isUpperAddress && !m_LinearAlgorithm)
+ // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
+ // 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;
}
@@ -9440,65 +9475,55 @@
if(!canMakeOtherLost || canCreateNewBlock)
{
// 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.
- for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
- {
- 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);
+ VmaAllocationCreateFlags allocFlagsCopy = createInfo.flags;
+ allocFlagsCopy &= ~VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT;
- 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,
+ if(m_LinearAlgorithm)
+ {
+ // Use only last block.
+ if(!m_Blocks.empty())
+ {
+ VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back();
+ VMA_ASSERT(pCurrBlock);
+ VkResult res = AllocateFromBlock(
pCurrBlock,
- currRequest.offset,
- alignment,
+ hCurrentPool,
+ currentFrameIndex,
size,
+ alignment,
+ allocFlagsCopy,
+ createInfo.pUserData,
suballocType,
- mapped,
- (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
- 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)
+ pAllocation);
+ if(res == VK_SUCCESS)
{
- 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_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
+ VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
+ return VK_SUCCESS;
}
- return VK_SUCCESS;
}
}
@@ -9555,56 +9580,24 @@
VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex];
VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size);
- if(mapped)
- {
- res = pBlock->Map(m_hAllocator, 1, VMA_NULL);
- 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(
+ res = AllocateFromBlock(
+ pBlock,
+ hCurrentPool,
currentFrameIndex,
- m_FrameInUseCount,
- m_BufferImageGranularity,
size,
alignment,
- isUpperAddress,
+ allocFlagsCopy,
+ createInfo.pUserData,
suballocType,
- false, // canMakeOtherLost
- &allocRequest))
+ pAllocation);
+ if(res == VK_SUCCESS)
{
- *pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
- 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.");
- }
+ VMA_DEBUG_LOG(" Created new block Size=%llu", newBlockSize);
return VK_SUCCESS;
}
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;
}
}
@@ -9819,17 +9812,93 @@
void VmaBlockVector::IncrementallySortBlocks()
{
- // Bubble sort only until first swap.
- for(size_t i = 1; i < m_Blocks.size(); ++i)
+ if(!m_LinearAlgorithm)
{
- 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]);
- return;
+ if(m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize())
+ {
+ 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)
{
VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
@@ -11812,10 +11881,9 @@
if(newCreateInfo.maxBlockCount == 0)
{
- newCreateInfo.maxBlockCount = isLinearAlgorithm ? 1 : SIZE_MAX;
+ newCreateInfo.maxBlockCount = SIZE_MAX;
}
- if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount ||
- isLinearAlgorithm && newCreateInfo.maxBlockCount > 1)
+ if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount)
{
return VK_ERROR_INITIALIZATION_FAILED;
}