mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2024-11-05 12:20:07 +00:00
Optimized VmaBlockVector::Allocate for certain cases.
This commit is contained in:
parent
0dec444a58
commit
1f6c388348
@ -9169,9 +9169,16 @@ VkResult VmaBlockVector::Allocate(
|
|||||||
VmaSuballocationType suballocType,
|
VmaSuballocationType suballocType,
|
||||||
VmaAllocation* pAllocation)
|
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;
|
||||||
|
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.
|
// Upper address can only be used with linear allocator.
|
||||||
if((createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0 &&
|
if(isUpperAddress && !m_LinearAlgorithm)
|
||||||
!m_LinearAlgorithm)
|
|
||||||
{
|
{
|
||||||
return VK_ERROR_FEATURE_NOT_PRESENT;
|
return VK_ERROR_FEATURE_NOT_PRESENT;
|
||||||
}
|
}
|
||||||
@ -9182,169 +9189,64 @@ VkResult VmaBlockVector::Allocate(
|
|||||||
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 upperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0;
|
|
||||||
|
|
||||||
VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
|
VmaMutexLock lock(m_Mutex, m_hAllocator->m_UseMutex);
|
||||||
|
|
||||||
// 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.
|
Under certain condition, this whole section can be skipped for optimization, so
|
||||||
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
|
we move on directly to trying to allocate with canMakeOtherLost. That's the case
|
||||||
|
e.g. for custom pools with linear algorithm.
|
||||||
|
*/
|
||||||
|
if(!canMakeOtherLost || canCreateNewBlock)
|
||||||
{
|
{
|
||||||
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
|
// 1. Search existing allocations. Try to allocate without making other allocations lost.
|
||||||
VMA_ASSERT(pCurrBlock);
|
// Forward order in m_Blocks - prefer blocks with smallest amount of free space.
|
||||||
VmaAllocationRequest currRequest = {};
|
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
|
||||||
if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
|
|
||||||
currentFrameIndex,
|
|
||||||
m_FrameInUseCount,
|
|
||||||
m_BufferImageGranularity,
|
|
||||||
size,
|
|
||||||
alignment,
|
|
||||||
upperAddress,
|
|
||||||
suballocType,
|
|
||||||
false, // canMakeOtherLost
|
|
||||||
&currRequest))
|
|
||||||
{
|
{
|
||||||
// Allocate from pCurrBlock.
|
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
|
||||||
VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
|
VMA_ASSERT(pCurrBlock);
|
||||||
|
VmaAllocationRequest currRequest = {};
|
||||||
if(mapped)
|
if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
|
||||||
{
|
|
||||||
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, upperAddress, *pAllocation);
|
|
||||||
(*pAllocation)->InitBlockAllocation(
|
|
||||||
hCurrentPool,
|
|
||||||
pCurrBlock,
|
|
||||||
currRequest.offset,
|
|
||||||
alignment,
|
|
||||||
size,
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED);
|
|
||||||
}
|
|
||||||
if(IsCorruptionDetectionEnabled())
|
|
||||||
{
|
|
||||||
VkResult res = pCurrBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
|
|
||||||
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
|
|
||||||
}
|
|
||||||
return VK_SUCCESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool canCreateNewBlock =
|
|
||||||
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) &&
|
|
||||||
(m_Blocks.size() < m_MaxBlockCount);
|
|
||||||
|
|
||||||
// 2. Try to create new block.
|
|
||||||
if(canCreateNewBlock)
|
|
||||||
{
|
|
||||||
// Calculate optimal size for new block.
|
|
||||||
VkDeviceSize newBlockSize = m_PreferredBlockSize;
|
|
||||||
uint32_t newBlockSizeShift = 0;
|
|
||||||
const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
|
|
||||||
|
|
||||||
// Allocating blocks of other sizes is allowed only in default pools.
|
|
||||||
// In custom pools block size is fixed.
|
|
||||||
if(m_IsCustomPool == false)
|
|
||||||
{
|
|
||||||
// Allocate 1/8, 1/4, 1/2 as first blocks.
|
|
||||||
const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
|
|
||||||
for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
|
|
||||||
{
|
|
||||||
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
|
|
||||||
if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
|
|
||||||
{
|
|
||||||
newBlockSize = smallerNewBlockSize;
|
|
||||||
++newBlockSizeShift;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t newBlockIndex = 0;
|
|
||||||
VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
|
|
||||||
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
|
|
||||||
if(m_IsCustomPool == false)
|
|
||||||
{
|
|
||||||
while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
|
|
||||||
{
|
|
||||||
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
|
|
||||||
if(smallerNewBlockSize >= size)
|
|
||||||
{
|
|
||||||
newBlockSize = smallerNewBlockSize;
|
|
||||||
++newBlockSizeShift;
|
|
||||||
res = CreateBlock(newBlockSize, &newBlockIndex);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(res == VK_SUCCESS)
|
|
||||||
{
|
|
||||||
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(
|
|
||||||
currentFrameIndex,
|
currentFrameIndex,
|
||||||
m_FrameInUseCount,
|
m_FrameInUseCount,
|
||||||
m_BufferImageGranularity,
|
m_BufferImageGranularity,
|
||||||
size,
|
size,
|
||||||
alignment,
|
alignment,
|
||||||
upperAddress,
|
isUpperAddress,
|
||||||
suballocType,
|
suballocType,
|
||||||
false, // canMakeOtherLost
|
false, // canMakeOtherLost
|
||||||
&allocRequest))
|
&currRequest))
|
||||||
{
|
{
|
||||||
|
// Allocate from pCurrBlock.
|
||||||
|
VMA_ASSERT(currRequest.itemsToMakeLostCount == 0);
|
||||||
|
|
||||||
|
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);
|
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
|
||||||
pBlock->m_pMetadata->Alloc(allocRequest, suballocType, size, upperAddress, *pAllocation);
|
pCurrBlock->m_pMetadata->Alloc(currRequest, suballocType, size, isUpperAddress, *pAllocation);
|
||||||
(*pAllocation)->InitBlockAllocation(
|
(*pAllocation)->InitBlockAllocation(
|
||||||
hCurrentPool,
|
hCurrentPool,
|
||||||
pBlock,
|
pCurrBlock,
|
||||||
allocRequest.offset,
|
currRequest.offset,
|
||||||
alignment,
|
alignment,
|
||||||
size,
|
size,
|
||||||
suballocType,
|
suballocType,
|
||||||
mapped,
|
mapped,
|
||||||
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
|
(createInfo.flags & VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT) != 0);
|
||||||
VMA_HEAVY_ASSERT(pBlock->Validate());
|
VMA_HEAVY_ASSERT(pCurrBlock->Validate());
|
||||||
VMA_DEBUG_LOG(" Created new allocation Size=%llu", allocInfo.allocationSize);
|
VMA_DEBUG_LOG(" Returned from existing allocation #%u", (uint32_t)blockIndex);
|
||||||
(*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
|
(*pAllocation)->SetUserData(m_hAllocator, createInfo.pUserData);
|
||||||
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
|
if(VMA_DEBUG_INITIALIZE_ALLOCATIONS)
|
||||||
{
|
{
|
||||||
@ -9352,21 +9254,124 @@ VkResult VmaBlockVector::Allocate(
|
|||||||
}
|
}
|
||||||
if(IsCorruptionDetectionEnabled())
|
if(IsCorruptionDetectionEnabled())
|
||||||
{
|
{
|
||||||
res = pBlock->WriteMagicValueAroundAllocation(m_hAllocator, allocRequest.offset, size);
|
VkResult res = pCurrBlock->WriteMagicValueAroundAllocation(m_hAllocator, currRequest.offset, size);
|
||||||
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
|
VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value.");
|
||||||
}
|
}
|
||||||
return VK_SUCCESS;
|
return VK_SUCCESS;
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
|
||||||
|
// 2. Try to create new block.
|
||||||
|
if(canCreateNewBlock)
|
||||||
|
{
|
||||||
|
// Calculate optimal size for new block.
|
||||||
|
VkDeviceSize newBlockSize = m_PreferredBlockSize;
|
||||||
|
uint32_t newBlockSizeShift = 0;
|
||||||
|
const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3;
|
||||||
|
|
||||||
|
// Allocating blocks of other sizes is allowed only in default pools.
|
||||||
|
// In custom pools block size is fixed.
|
||||||
|
if(m_IsCustomPool == false)
|
||||||
{
|
{
|
||||||
// Allocation from empty block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
|
// Allocate 1/8, 1/4, 1/2 as first blocks.
|
||||||
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize();
|
||||||
|
for(uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i)
|
||||||
|
{
|
||||||
|
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
|
||||||
|
if(smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2)
|
||||||
|
{
|
||||||
|
newBlockSize = smallerNewBlockSize;
|
||||||
|
++newBlockSizeShift;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t newBlockIndex = 0;
|
||||||
|
VkResult res = CreateBlock(newBlockSize, &newBlockIndex);
|
||||||
|
// Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize.
|
||||||
|
if(m_IsCustomPool == false)
|
||||||
|
{
|
||||||
|
while(res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX)
|
||||||
|
{
|
||||||
|
const VkDeviceSize smallerNewBlockSize = newBlockSize / 2;
|
||||||
|
if(smallerNewBlockSize >= size)
|
||||||
|
{
|
||||||
|
newBlockSize = smallerNewBlockSize;
|
||||||
|
++newBlockSizeShift;
|
||||||
|
res = CreateBlock(newBlockSize, &newBlockIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(res == VK_SUCCESS)
|
||||||
|
{
|
||||||
|
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(
|
||||||
|
currentFrameIndex,
|
||||||
|
m_FrameInUseCount,
|
||||||
|
m_BufferImageGranularity,
|
||||||
|
size,
|
||||||
|
alignment,
|
||||||
|
isUpperAddress,
|
||||||
|
suballocType,
|
||||||
|
false, // canMakeOtherLost
|
||||||
|
&allocRequest))
|
||||||
|
{
|
||||||
|
*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.");
|
||||||
|
}
|
||||||
|
return VK_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Allocation from empty block failed, possibly due to VMA_DEBUG_MARGIN or alignment.
|
||||||
|
return VK_ERROR_OUT_OF_DEVICE_MEMORY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool canMakeOtherLost = (createInfo.flags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) != 0;
|
|
||||||
|
|
||||||
// 3. Try to allocate from existing blocks with making other allocations lost.
|
// 3. Try to allocate from existing blocks with making other allocations lost.
|
||||||
if(canMakeOtherLost)
|
if(canMakeOtherLost)
|
||||||
{
|
{
|
||||||
@ -9434,7 +9439,7 @@ VkResult VmaBlockVector::Allocate(
|
|||||||
}
|
}
|
||||||
// Allocate from this pBlock.
|
// Allocate from this pBlock.
|
||||||
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
|
*pAllocation = vma_new(m_hAllocator, VmaAllocation_T)(currentFrameIndex, isUserDataString);
|
||||||
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, upperAddress, *pAllocation);
|
pBestRequestBlock->m_pMetadata->Alloc(bestRequest, suballocType, size, isUpperAddress, *pAllocation);
|
||||||
(*pAllocation)->InitBlockAllocation(
|
(*pAllocation)->InitBlockAllocation(
|
||||||
hCurrentPool,
|
hCurrentPool,
|
||||||
pBestRequestBlock,
|
pBestRequestBlock,
|
||||||
|
Loading…
Reference in New Issue
Block a user