Added VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT, WORST_FIT, FIRST_FIT, and aliases: VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, MIN_TIME, MIN_FRAGMENTATION.

Deleted VMA_BEST_FIT macro.
This commit is contained in:
Adam Sawicki 2018-08-24 16:28:28 +02:00
parent 70a683e53f
commit 1852036194

View File

@ -1806,6 +1806,29 @@ typedef enum VmaAllocationCreateFlagBits {
*/ */
VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040,
/** Allocation strategy that chooses smallest possible free range for the
allocation.
*/
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = 0x00010000,
/** Allocation strategy that chooses biggest possible free range for the
allocation.
*/
VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT = 0x00020000,
/** Allocation strategy that chooses first suitable free range for the
allocation.
*/
VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = 0x00040000,
/** Allocation strategy that tries to minimize memory usage.
*/
VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
/** Allocation strategy that tries to minimize allocation time.
*/
VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT,
/** Allocation strategy that tries to minimize memory fragmentation.
*/
VMA_ALLOCATION_CREATE_STRATEGY_MIN_FRAGMENTATION_BIT = VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT,
VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VmaAllocationCreateFlagBits; } VmaAllocationCreateFlagBits;
typedef VkFlags VmaAllocationCreateFlags; typedef VkFlags VmaAllocationCreateFlags;
@ -2793,22 +2816,6 @@ If providing your own implementation, you need to implement a subset of std::ato
#define VMA_ATOMIC_UINT32 std::atomic<uint32_t> #define VMA_ATOMIC_UINT32 std::atomic<uint32_t>
#endif #endif
#ifndef VMA_BEST_FIT
/**
Main parameter for function assessing how good is a free suballocation for a new
allocation request.
- Set to 1 to use Best-Fit algorithm - prefer smaller blocks, as close to the
size of requested allocations as possible.
- Set to 0 to use Worst-Fit algorithm - prefer larger blocks, as large as
possible.
Experiments in special testing environment showed that Best-Fit algorithm is
better.
*/
#define VMA_BEST_FIT (1)
#endif
#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY #ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY
/** /**
Every allocation will have its own memory block. Every allocation will have its own memory block.
@ -4536,6 +4543,7 @@ public:
bool upperAddress, bool upperAddress,
VmaSuballocationType allocType, VmaSuballocationType allocType,
bool canMakeOtherLost, bool canMakeOtherLost,
uint32_t strategy, // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* flags.
VmaAllocationRequest* pAllocationRequest) = 0; VmaAllocationRequest* pAllocationRequest) = 0;
virtual bool MakeRequestedAllocationsLost( virtual bool MakeRequestedAllocationsLost(
@ -4608,6 +4616,7 @@ public:
bool upperAddress, bool upperAddress,
VmaSuballocationType allocType, VmaSuballocationType allocType,
bool canMakeOtherLost, bool canMakeOtherLost,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest); VmaAllocationRequest* pAllocationRequest);
virtual bool MakeRequestedAllocationsLost( virtual bool MakeRequestedAllocationsLost(
@ -4776,6 +4785,7 @@ public:
bool upperAddress, bool upperAddress,
VmaSuballocationType allocType, VmaSuballocationType allocType,
bool canMakeOtherLost, bool canMakeOtherLost,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest); VmaAllocationRequest* pAllocationRequest);
virtual bool MakeRequestedAllocationsLost( virtual bool MakeRequestedAllocationsLost(
@ -5036,6 +5046,7 @@ private:
VmaAllocationCreateFlags allocFlags, VmaAllocationCreateFlags allocFlags,
void* pUserData, void* pUserData,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
uint32_t strategy,
VmaAllocation* pAllocation); VmaAllocation* pAllocation);
VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex);
@ -6576,16 +6587,6 @@ void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json) const
#endif // #if VMA_STATS_STRING_ENABLED #endif // #if VMA_STATS_STRING_ENABLED
/*
How many suitable free suballocations to analyze before choosing best one.
- Set to 1 to use First-Fit algorithm - first suitable free suballocation will
be chosen.
- Set to UINT32_MAX to use Best-Fit/Worst-Fit algorithm - all suitable free
suballocations will be analized and best one will be chosen.
- Any other value is also acceptable.
*/
//static const uint32_t MAX_SUITABLE_SUBALLOCATIONS_TO_CHECK = 8;
bool VmaBlockMetadata_Generic::CreateAllocationRequest( bool VmaBlockMetadata_Generic::CreateAllocationRequest(
uint32_t currentFrameIndex, uint32_t currentFrameIndex,
uint32_t frameInUseCount, uint32_t frameInUseCount,
@ -6595,6 +6596,7 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
bool upperAddress, bool upperAddress,
VmaSuballocationType allocType, VmaSuballocationType allocType,
bool canMakeOtherLost, bool canMakeOtherLost,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) VmaAllocationRequest* pAllocationRequest)
{ {
VMA_ASSERT(allocSize > 0); VMA_ASSERT(allocSize > 0);
@ -6604,7 +6606,8 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
VMA_HEAVY_ASSERT(Validate()); VMA_HEAVY_ASSERT(Validate());
// There is not enough total free space in this block to fullfill the request: Early return. // There is not enough total free space in this block to fullfill the request: Early return.
if(canMakeOtherLost == false && m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN) if(canMakeOtherLost == false &&
m_SumFreeSize < allocSize + 2 * VMA_DEBUG_MARGIN)
{ {
return false; return false;
} }
@ -6613,7 +6616,7 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); const size_t freeSuballocCount = m_FreeSuballocationsBySize.size();
if(freeSuballocCount > 0) if(freeSuballocCount > 0)
{ {
if(VMA_BEST_FIT) if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
{ {
// Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN. // Find first free suballocation with size not less than allocSize + 2 * VMA_DEBUG_MARGIN.
VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess(
@ -6643,7 +6646,7 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
} }
} }
} }
else else // WORST_FIT, FIRST_FIT
{ {
// Search staring from biggest suballocations. // Search staring from biggest suballocations.
for(size_t index = freeSuballocCount; index--; ) for(size_t index = freeSuballocCount; index--; )
@ -6700,7 +6703,8 @@ bool VmaBlockMetadata_Generic::CreateAllocationRequest(
{ {
tmpAllocRequest.item = suballocIt; tmpAllocRequest.item = suballocIt;
if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost()) if(tmpAllocRequest.CalcCost() < pAllocationRequest->CalcCost() ||
strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
{ {
*pAllocationRequest = tmpAllocRequest; *pAllocationRequest = tmpAllocRequest;
} }
@ -8306,6 +8310,7 @@ bool VmaBlockMetadata_Linear::CreateAllocationRequest(
bool upperAddress, bool upperAddress,
VmaSuballocationType allocType, VmaSuballocationType allocType,
bool canMakeOtherLost, bool canMakeOtherLost,
uint32_t strategy,
VmaAllocationRequest* pAllocationRequest) VmaAllocationRequest* pAllocationRequest)
{ {
VMA_ASSERT(allocSize > 0); VMA_ASSERT(allocSize > 0);
@ -9444,6 +9449,10 @@ VkResult VmaBlockVector::Allocate(
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);
uint32_t strategy = createInfo.flags & (
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT |
VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT |
VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT);
// If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer. // If linearAlgorithm is used, canMakeOtherLost is available only when used as ring buffer.
// Which in turn is available only when maxBlockCount = 1. // Which in turn is available only when maxBlockCount = 1.
@ -9459,6 +9468,20 @@ VkResult VmaBlockVector::Allocate(
return VK_ERROR_FEATURE_NOT_PRESENT; return VK_ERROR_FEATURE_NOT_PRESENT;
} }
// Validate strategy.
switch(strategy)
{
case 0:
strategy = VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT;
break;
case VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT:
case VMA_ALLOCATION_CREATE_STRATEGY_WORST_FIT_BIT:
case VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT:
break;
default:
return VK_ERROR_FEATURE_NOT_PRESENT;
}
// Early reject: requested allocation size is larger that maximum block size for this block vector. // Early reject: requested allocation size is larger that maximum block size for this block vector.
if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize) if(size + 2 * VMA_DEBUG_MARGIN > m_PreferredBlockSize)
{ {
@ -9494,6 +9517,7 @@ VkResult VmaBlockVector::Allocate(
allocFlagsCopy, allocFlagsCopy,
createInfo.pUserData, createInfo.pUserData,
suballocType, suballocType,
strategy,
pAllocation); pAllocation);
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
{ {
@ -9504,25 +9528,54 @@ VkResult VmaBlockVector::Allocate(
} }
else else
{ {
// Forward order in m_Blocks - prefer blocks with smallest amount of free space. if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
{ {
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
VMA_ASSERT(pCurrBlock); for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
VkResult res = AllocateFromBlock(
pCurrBlock,
hCurrentPool,
currentFrameIndex,
size,
alignment,
allocFlagsCopy,
createInfo.pUserData,
suballocType,
pAllocation);
if(res == VK_SUCCESS)
{ {
VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex); VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
return VK_SUCCESS; VMA_ASSERT(pCurrBlock);
VkResult res = AllocateFromBlock(
pCurrBlock,
hCurrentPool,
currentFrameIndex,
size,
alignment,
allocFlagsCopy,
createInfo.pUserData,
suballocType,
strategy,
pAllocation);
if(res == VK_SUCCESS)
{
VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
return VK_SUCCESS;
}
}
}
else // WORST_FIT, FIRST_FIT
{
// Backward order in m_Blocks - prefer blocks with largest amount of free space.
for(size_t 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,
strategy,
pAllocation);
if(res == VK_SUCCESS)
{
VMA_DEBUG_LOG(" Returned from existing block #%u", (uint32_t)blockIndex);
return VK_SUCCESS;
}
} }
} }
} }
@ -9589,6 +9642,7 @@ VkResult VmaBlockVector::Allocate(
allocFlagsCopy, allocFlagsCopy,
createInfo.pUserData, createInfo.pUserData,
suballocType, suballocType,
strategy,
pAllocation); pAllocation);
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
{ {
@ -9615,34 +9669,76 @@ VkResult VmaBlockVector::Allocate(
VkDeviceSize bestRequestCost = VK_WHOLE_SIZE; VkDeviceSize bestRequestCost = VK_WHOLE_SIZE;
// 1. Search existing allocations. // 1. Search existing allocations.
// Forward order in m_Blocks - prefer blocks with smallest amount of free space. if(strategy == VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT)
for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
{ {
VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; // Forward order in m_Blocks - prefer blocks with smallest amount of free space.
VMA_ASSERT(pCurrBlock); for(size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex )
VmaAllocationRequest currRequest = {};
if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
currentFrameIndex,
m_FrameInUseCount,
m_BufferImageGranularity,
size,
alignment,
(createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
suballocType,
canMakeOtherLost,
&currRequest))
{ {
const VkDeviceSize currRequestCost = currRequest.CalcCost(); VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex];
if(pBestRequestBlock == VMA_NULL || VMA_ASSERT(pCurrBlock);
currRequestCost < bestRequestCost) VmaAllocationRequest currRequest = {};
if(pCurrBlock->m_pMetadata->CreateAllocationRequest(
currentFrameIndex,
m_FrameInUseCount,
m_BufferImageGranularity,
size,
alignment,
(createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
suballocType,
canMakeOtherLost,
strategy,
&currRequest))
{ {
pBestRequestBlock = pCurrBlock; const VkDeviceSize currRequestCost = currRequest.CalcCost();
bestRequest = currRequest; if(pBestRequestBlock == VMA_NULL ||
bestRequestCost = currRequestCost; currRequestCost < bestRequestCost)
if(bestRequestCost == 0)
{ {
break; pBestRequestBlock = pCurrBlock;
bestRequest = currRequest;
bestRequestCost = currRequestCost;
if(bestRequestCost == 0)
{
break;
}
}
}
}
}
else // WORST_FIT, FIRST_FIT
{
// Backward order in m_Blocks - prefer blocks with largest amount of free space.
for(size_t 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,
(createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0,
suballocType,
canMakeOtherLost,
strategy,
&currRequest))
{
const VkDeviceSize currRequestCost = currRequest.CalcCost();
if(pBestRequestBlock == VMA_NULL ||
currRequestCost < bestRequestCost ||
strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
{
pBestRequestBlock = pCurrBlock;
bestRequest = currRequest;
bestRequestCost = currRequestCost;
if(bestRequestCost == 0 ||
strategy == VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT)
{
break;
}
} }
} }
} }
@ -9835,6 +9931,7 @@ VkResult VmaBlockVector::AllocateFromBlock(
VmaAllocationCreateFlags allocFlags, VmaAllocationCreateFlags allocFlags,
void* pUserData, void* pUserData,
VmaSuballocationType suballocType, VmaSuballocationType suballocType,
uint32_t strategy,
VmaAllocation* pAllocation) VmaAllocation* pAllocation)
{ {
VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0); VMA_ASSERT((allocFlags & VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT) == 0);
@ -9852,6 +9949,7 @@ VkResult VmaBlockVector::AllocateFromBlock(
isUpperAddress, isUpperAddress,
suballocType, suballocType,
false, // canMakeOtherLost false, // canMakeOtherLost
strategy,
&currRequest)) &currRequest))
{ {
// Allocate from pCurrBlock. // Allocate from pCurrBlock.
@ -10262,6 +10360,7 @@ VkResult VmaDefragmentator::DefragmentRound(
false, // upperAddress false, // upperAddress
suballocType, suballocType,
false, // canMakeOtherLost false, // canMakeOtherLost
VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT,
&dstAllocRequest) && &dstAllocRequest) &&
MoveMakesSense( MoveMakesSense(
dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset)) dstBlockIndex, dstAllocRequest.offset, srcBlockIndex, srcOffset))