Refactored defragmentation code. Removed class VmaDefragmentator. Renamed struct to VmaDefragmentationMove.

Minor fixes in documentation and comments.
This commit is contained in:
Adam Sawicki 2018-10-16 15:30:55 +02:00
parent ef6cc40b59
commit a114419b23

View file

@ -1447,7 +1447,7 @@ Features deliberately excluded from the scope of this library:
yourself. Any explicit support for sparse binding/residency would rather yourself. Any explicit support for sparse binding/residency would rather
require another, higher-level library on top of VMA. require another, higher-level library on top of VMA.
- Data transfer - issuing commands that transfer data between buffers or images, any usage of - Data transfer - issuing commands that transfer data between buffers or images, any usage of
`VkCommandList` or `VkQueue` and related synchronization is responsibility of the user. `VkCommandBuffer` or `VkQueue` and related synchronization is responsibility of the user.
- Allocations for imported/exported external memory. They tend to require - Allocations for imported/exported external memory. They tend to require
explicit memory type index and dedicated allocation anyway, so they don't explicit memory type index and dedicated allocation anyway, so they don't
interact with main features of this library. Such special purpose allocations interact with main features of this library. Such special purpose allocations
@ -2600,6 +2600,15 @@ typedef struct VmaDefragmentationInfo2 {
`UINT32_MAX` means no limit. `UINT32_MAX` means no limit.
*/ */
uint32_t maxGpuAllocationsToMove; uint32_t maxGpuAllocationsToMove;
/** \brief Optional. Command buffer where GPU copy commands will be posted.
If not null, it must be a valid command buffer handle that supports Transfer queue type.
It must be in the recording state and outside of a render pass instance.
You need to submit it and make sure it finished execution before calling vmaDefragmentationEnd().
Passing null means that only CPU defragmentation will be performed.
*/
VkCommandBuffer commandBuffer;
} VmaDefragmentationInfo2; } VmaDefragmentationInfo2;
/** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment(). /** \brief Deprecated. Optional configuration parameters to be passed to function vmaDefragment().
@ -5453,7 +5462,16 @@ struct VmaPointerLess
} }
}; };
class VmaDefragmentator; struct VmaDefragmentationMove
{
size_t srcBlockIndex;
size_t dstBlockIndex;
VkDeviceSize srcOffset;
VkDeviceSize dstOffset;
VkDeviceSize size;
};
class VmaDefragmentationAlgorithm;
/* /*
Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific
@ -5465,6 +5483,9 @@ struct VmaBlockVector
{ {
VMA_CLASS_NO_COPY(VmaBlockVector) VMA_CLASS_NO_COPY(VmaBlockVector)
public: public:
// Used temporarily during defragmentation.
VmaDefragmentationAlgorithm* m_pDefragmentationAlgorithm;
VmaBlockVector( VmaBlockVector(
VmaAllocator hAllocator, VmaAllocator hAllocator,
uint32_t memoryTypeIndex, uint32_t memoryTypeIndex,
@ -5515,11 +5536,7 @@ public:
size_t* pLostAllocationCount); size_t* pLostAllocationCount);
VkResult CheckCorruption(); VkResult CheckCorruption();
VmaDefragmentator* EnsureDefragmentator( // If m_pDefragmentationAlgorithm is not null, uses it to defragment and destroys it.
VmaAllocator hAllocator,
uint32_t currentFrameIndex);
// If m_pDefragmentator is not null, uses it to defragment and destroys it.
VkResult Defragment( VkResult Defragment(
VmaDefragmentationStats* pDefragmentationStats, VmaDefragmentationStats* pDefragmentationStats,
VkDeviceSize& maxBytesToMove, VkDeviceSize& maxBytesToMove,
@ -5527,7 +5544,6 @@ public:
private: private:
friend class VmaDefragmentationAlgorithm; friend class VmaDefragmentationAlgorithm;
friend class VmaDefragmentator;
const VmaAllocator m_hAllocator; const VmaAllocator m_hAllocator;
const uint32_t m_MemoryTypeIndex; const uint32_t m_MemoryTypeIndex;
@ -5539,14 +5555,13 @@ private:
const bool m_IsCustomPool; const bool m_IsCustomPool;
const bool m_ExplicitBlockSize; const bool m_ExplicitBlockSize;
const uint32_t m_Algorithm; const uint32_t m_Algorithm;
/* There can be at most one allocation that is completely empty - a
hysteresis to avoid pessimistic case of alternating creation and destruction
of a VkDeviceMemory. */
bool m_HasEmptyBlock; bool m_HasEmptyBlock;
VMA_RW_MUTEX m_Mutex; VMA_RW_MUTEX m_Mutex;
// Incrementally sorted by sumFreeSize, ascending. // Incrementally sorted by sumFreeSize, ascending.
VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks; VmaVector< VmaDeviceMemoryBlock*, VmaStlAllocator<VmaDeviceMemoryBlock*> > m_Blocks;
/* There can be at most one allocation that is completely empty - a
hysteresis to avoid pessimistic case of alternating creation and destruction
of a VkDeviceMemory. */
VmaDefragmentator* m_pDefragmentator;
uint32_t m_NextBlockId; uint32_t m_NextBlockId;
VkDeviceSize CalcMaxBlockSize() const; VkDeviceSize CalcMaxBlockSize() const;
@ -5597,19 +5612,17 @@ private:
uint32_t m_Id; uint32_t m_Id;
}; };
/*
Performs defragmentation:
- Updates `pBlockVector->m_pMetadata`.
- Updates allocations by calling ChangeBlockAllocation().
- Does not move actual data, only returns requested moves as `moves`.
*/
class VmaDefragmentationAlgorithm class VmaDefragmentationAlgorithm
{ {
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm) VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
public: public:
struct Move
{
size_t srcBlockIndex;
size_t dstBlockIndex;
VkDeviceSize srcOffset;
VkDeviceSize dstOffset;
VkDeviceSize size;
};
VmaDefragmentationAlgorithm( VmaDefragmentationAlgorithm(
VmaAllocator hAllocator, VmaAllocator hAllocator,
VmaBlockVector* pBlockVector, VmaBlockVector* pBlockVector,
@ -5619,7 +5632,7 @@ public:
void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged); void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
VkResult Defragment( VkResult Defragment(
VmaVector< Move, VmaStlAllocator<Move> >& moves, VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
VkDeviceSize maxBytesToMove, VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove); uint32_t maxAllocationsToMove);
@ -5723,7 +5736,7 @@ private:
BlockInfoVector m_Blocks; BlockInfoVector m_Blocks;
VkResult DefragmentRound( VkResult DefragmentRound(
VmaVector< Move, VmaStlAllocator<Move> >& moves, VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
VkDeviceSize maxBytesToMove, VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove); uint32_t maxAllocationsToMove);
@ -5733,33 +5746,6 @@ private:
}; };
class VmaDefragmentator
{
VMA_CLASS_NO_COPY(VmaDefragmentator)
private:
const VmaAllocator m_hAllocator;
VmaBlockVector* const m_pBlockVector;
uint32_t m_CurrentFrameIndex;
VmaDefragmentationAlgorithm* m_pAlgorithm;
public:
VmaDefragmentator(
VmaAllocator hAllocator,
VmaBlockVector* pBlockVector,
uint32_t currentFrameIndex);
~VmaDefragmentator();
VkDeviceSize GetBytesMoved() const { return m_pAlgorithm->GetBytesMoved(); }
uint32_t GetAllocationsMoved() const { return m_pAlgorithm->GetAllocationsMoved(); }
void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
VkResult Defragment(
VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove);
};
struct VmaDefragmentationContext_T struct VmaDefragmentationContext_T
{ {
VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
@ -10378,6 +10364,7 @@ VmaBlockVector::VmaBlockVector(
bool isCustomPool, bool isCustomPool,
bool explicitBlockSize, bool explicitBlockSize,
uint32_t algorithm) : uint32_t algorithm) :
m_pDefragmentationAlgorithm(VMA_NULL),
m_hAllocator(hAllocator), m_hAllocator(hAllocator),
m_MemoryTypeIndex(memoryTypeIndex), m_MemoryTypeIndex(memoryTypeIndex),
m_PreferredBlockSize(preferredBlockSize), m_PreferredBlockSize(preferredBlockSize),
@ -10390,14 +10377,13 @@ VmaBlockVector::VmaBlockVector(
m_Algorithm(algorithm), m_Algorithm(algorithm),
m_HasEmptyBlock(false), m_HasEmptyBlock(false),
m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())), m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
m_pDefragmentator(VMA_NULL),
m_NextBlockId(0) m_NextBlockId(0)
{ {
} }
VmaBlockVector::~VmaBlockVector() VmaBlockVector::~VmaBlockVector()
{ {
VMA_ASSERT(m_pDefragmentator == VMA_NULL); VMA_ASSERT(m_pDefragmentationAlgorithm == VMA_NULL);
for(size_t i = m_Blocks.size(); i--; ) for(size_t i = m_Blocks.size(); i--; )
{ {
@ -11112,27 +11098,12 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
#endif // #if VMA_STATS_STRING_ENABLED #endif // #if VMA_STATS_STRING_ENABLED
VmaDefragmentator* VmaBlockVector::EnsureDefragmentator(
VmaAllocator hAllocator,
uint32_t currentFrameIndex)
{
if(m_pDefragmentator == VMA_NULL)
{
m_pDefragmentator = vma_new(m_hAllocator, VmaDefragmentator)(
hAllocator,
this,
currentFrameIndex);
}
return m_pDefragmentator;
}
VkResult VmaBlockVector::Defragment( VkResult VmaBlockVector::Defragment(
VmaDefragmentationStats* pDefragmentationStats, VmaDefragmentationStats* pDefragmentationStats,
VkDeviceSize& maxBytesToMove, VkDeviceSize& maxBytesToMove,
uint32_t& maxAllocationsToMove) uint32_t& maxAllocationsToMove)
{ {
if(m_pDefragmentator == VMA_NULL) if(!m_pDefragmentationAlgorithm)
{ {
return VK_SUCCESS; return VK_SUCCESS;
} }
@ -11140,13 +11111,134 @@ VkResult VmaBlockVector::Defragment(
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
// Defragment. // Defragment.
VkResult result = m_pDefragmentator->Defragment(maxBytesToMove, maxAllocationsToMove); VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> > moves =
VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >(VmaStlAllocator<VmaDefragmentationMove>(m_hAllocator->GetAllocationCallbacks()));
VkResult res = m_pDefragmentationAlgorithm->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
if(res < 0)
{
return res;
}
if(res >= VK_SUCCESS)
{
const size_t blockCount = m_Blocks.size();
const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_MemoryTypeIndex);
enum BLOCK_FLAG
{
BLOCK_FLAG_USED = 0x00000001,
BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
};
struct BlockInfo
{
uint32_t flags;
void* pMappedData;
};
VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
// Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
const size_t moveCount = moves.size();
for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
{
const VmaDefragmentationMove& move = moves[moveIndex];
blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
}
// Go over all blocks. Get mapped pointer or map if necessary.
for(size_t blockIndex = 0; (res >= 0) && (blockIndex < blockCount); ++blockIndex)
{
BlockInfo& currBlockInfo = blockInfo[blockIndex];
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
{
currBlockInfo.pMappedData = pBlock->GetMappedData();
// It is not originally mapped - map it.
if(currBlockInfo.pMappedData == VMA_NULL)
{
res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
if(res == VK_SUCCESS)
{
currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
}
}
}
}
// Go over all moves. Do actual data transfer.
if(res >= 0)
{
const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
{
const VmaDefragmentationMove& move = moves[moveIndex];
const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
// Invalidate source.
if(isNonCoherent)
{
VmaDeviceMemoryBlock* const pSrcBlock = m_Blocks[move.srcBlockIndex];
memRange.memory = pSrcBlock->GetDeviceMemory();
memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
memRange.size = VMA_MIN(
VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
(*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
}
// THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
memcpy(
reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
static_cast<size_t>(move.size));
if(IsCorruptionDetectionEnabled())
{
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
}
// Flush destination.
if(isNonCoherent)
{
VmaDeviceMemoryBlock* const pDstBlock = m_Blocks[move.dstBlockIndex];
memRange.memory = pDstBlock->GetDeviceMemory();
memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
memRange.size = VMA_MIN(
VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
pDstBlock->m_pMetadata->GetSize() - memRange.offset);
(*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
}
}
}
// Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
// Regardless of res >= 0.
for(size_t blockIndex = blockCount; blockIndex--; )
{
const BlockInfo& currBlockInfo = blockInfo[blockIndex];
if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
{
VmaDeviceMemoryBlock* pBlock = m_Blocks[blockIndex];
pBlock->Unmap(m_hAllocator, 1);
}
}
}
// Accumulate statistics. // Accumulate statistics.
if(pDefragmentationStats != VMA_NULL) if(pDefragmentationStats != VMA_NULL)
{ {
const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved(); const VkDeviceSize bytesMoved = m_pDefragmentationAlgorithm->GetBytesMoved();
const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved(); const uint32_t allocationsMoved = m_pDefragmentationAlgorithm->GetAllocationsMoved();
pDefragmentationStats->bytesMoved += bytesMoved; pDefragmentationStats->bytesMoved += bytesMoved;
pDefragmentationStats->allocationsMoved += allocationsMoved; pDefragmentationStats->allocationsMoved += allocationsMoved;
VMA_ASSERT(bytesMoved <= maxBytesToMove); VMA_ASSERT(bytesMoved <= maxBytesToMove);
@ -11156,7 +11248,7 @@ VkResult VmaBlockVector::Defragment(
} }
// Free empty blocks. // Free empty blocks.
if(result >= 0) if(res >= 0)
{ {
m_HasEmptyBlock = false; m_HasEmptyBlock = false;
for(size_t blockIndex = m_Blocks.size(); blockIndex--; ) for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
@ -11184,11 +11276,11 @@ VkResult VmaBlockVector::Defragment(
} }
} }
// Destroy defragmentator. // Destroy defragmentation algorithm object.
vma_delete(m_hAllocator, m_pDefragmentator); vma_delete(m_hAllocator, m_pDefragmentationAlgorithm);
m_pDefragmentator = VMA_NULL; m_pDefragmentationAlgorithm = VMA_NULL;
return result; return res;
} }
void VmaBlockVector::MakePoolAllocationsLost( void VmaBlockVector::MakePoolAllocationsLost(
@ -11284,7 +11376,7 @@ void VmaDefragmentationAlgorithm::AddAllocation(VmaAllocation hAlloc, VkBool32*
} }
VkResult VmaDefragmentationAlgorithm::DefragmentRound( VkResult VmaDefragmentationAlgorithm::DefragmentRound(
VmaVector< Move, VmaStlAllocator<Move> >& moves, VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
VkDeviceSize maxBytesToMove, VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove) uint32_t maxAllocationsToMove)
{ {
@ -11357,7 +11449,7 @@ VkResult VmaDefragmentationAlgorithm::DefragmentRound(
return VK_INCOMPLETE; return VK_INCOMPLETE;
} }
Move move; VmaDefragmentationMove move;
move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex; move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex; move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
move.srcOffset = srcOffset; move.srcOffset = srcOffset;
@ -11411,7 +11503,7 @@ VkResult VmaDefragmentationAlgorithm::DefragmentRound(
} }
VkResult VmaDefragmentationAlgorithm::Defragment( VkResult VmaDefragmentationAlgorithm::Defragment(
VmaVector< Move, VmaStlAllocator<Move> >& moves, VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
VkDeviceSize maxBytesToMove, VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove) uint32_t maxAllocationsToMove)
{ {
@ -11493,161 +11585,6 @@ bool VmaDefragmentationAlgorithm::MoveMakesSense(
return false; return false;
} }
////////////////////////////////////////////////////////////////////////////////
// VmaDefragmentator members definition
VmaDefragmentator::VmaDefragmentator(
VmaAllocator hAllocator,
VmaBlockVector* pBlockVector,
uint32_t currentFrameIndex) :
m_hAllocator(hAllocator),
m_pBlockVector(pBlockVector),
m_CurrentFrameIndex(currentFrameIndex),
m_pAlgorithm(vma_new(hAllocator, VmaDefragmentationAlgorithm)(
hAllocator, pBlockVector, currentFrameIndex))
{
VMA_ASSERT(pBlockVector->GetAlgorithm() == 0);
}
VmaDefragmentator::~VmaDefragmentator()
{
vma_delete(m_hAllocator, m_pAlgorithm);
}
void VmaDefragmentator::AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged)
{
m_pAlgorithm->AddAllocation(hAlloc, pChanged);
}
VkResult VmaDefragmentator::Defragment(
VkDeviceSize maxBytesToMove,
uint32_t maxAllocationsToMove)
{
typedef VmaDefragmentationAlgorithm::Move Move;
VmaVector< Move, VmaStlAllocator<Move> > moves =
VmaVector< Move, VmaStlAllocator<Move> >(VmaStlAllocator<Move>(m_hAllocator->GetAllocationCallbacks()));
VkResult res = m_pAlgorithm->Defragment(moves, maxBytesToMove, maxAllocationsToMove);
if(res < 0)
{
return res;
}
const size_t blockCount = m_pBlockVector->m_Blocks.size();
const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_pBlockVector->GetMemoryTypeIndex());
enum BLOCK_FLAG
{
BLOCK_FLAG_USED = 0x00000001,
BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION = 0x00000002,
};
struct BlockInfo
{
uint32_t flags;
void* pMappedData;
};
VmaVector< BlockInfo, VmaStlAllocator<BlockInfo> >
blockInfo(blockCount, VmaStlAllocator<BlockInfo>(m_hAllocator->GetAllocationCallbacks()));
memset(blockInfo.data(), 0, blockCount * sizeof(BlockInfo));
// Go over all moves. Mark blocks that are used with BLOCK_FLAG_USED.
const size_t moveCount = moves.size();
for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
{
const Move& move = moves[moveIndex];
blockInfo[move.srcBlockIndex].flags |= BLOCK_FLAG_USED;
blockInfo[move.dstBlockIndex].flags |= BLOCK_FLAG_USED;
}
// Go over all blocks. Get mapped pointer or map if necessary.
for(size_t blockIndex = 0; (res >= 0) && (blockIndex < blockCount); ++blockIndex)
{
BlockInfo& currBlockInfo = blockInfo[blockIndex];
VmaDeviceMemoryBlock* pBlock = m_pBlockVector->m_Blocks[blockIndex];
if((currBlockInfo.flags & BLOCK_FLAG_USED) != 0)
{
currBlockInfo.pMappedData = pBlock->GetMappedData();
// It is not originally mapped - map it.
if(currBlockInfo.pMappedData == VMA_NULL)
{
res = pBlock->Map(m_hAllocator, 1, &currBlockInfo.pMappedData);
if(res == VK_SUCCESS)
{
currBlockInfo.flags |= BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION;
}
}
}
}
// Go over all moves. Do actual data transfer.
if(res >= 0)
{
const VkDeviceSize nonCoherentAtomSize = m_hAllocator->m_PhysicalDeviceProperties.limits.nonCoherentAtomSize;
VkMappedMemoryRange memRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE };
for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
{
const Move& move = moves[moveIndex];
const BlockInfo& srcBlockInfo = blockInfo[move.srcBlockIndex];
const BlockInfo& dstBlockInfo = blockInfo[move.dstBlockIndex];
VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData);
// Invalidate source.
if(isNonCoherent)
{
VmaDeviceMemoryBlock* const pSrcBlock = m_pBlockVector->m_Blocks[move.srcBlockIndex];
memRange.memory = pSrcBlock->GetDeviceMemory();
memRange.offset = VmaAlignDown(move.srcOffset, nonCoherentAtomSize);
memRange.size = VMA_MIN(
VmaAlignUp(move.size + (move.srcOffset - memRange.offset), nonCoherentAtomSize),
pSrcBlock->m_pMetadata->GetSize() - memRange.offset);
(*m_hAllocator->GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
}
// THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
memcpy(
reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
static_cast<size_t>(move.size));
if(m_pBlockVector->IsCorruptionDetectionEnabled())
{
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size);
}
// Flush destination.
if(isNonCoherent)
{
VmaDeviceMemoryBlock* const pDstBlock = m_pBlockVector->m_Blocks[move.dstBlockIndex];
memRange.memory = pDstBlock->GetDeviceMemory();
memRange.offset = VmaAlignDown(move.dstOffset, nonCoherentAtomSize);
memRange.size = VMA_MIN(
VmaAlignUp(move.size + (move.dstOffset - memRange.offset), nonCoherentAtomSize),
pDstBlock->m_pMetadata->GetSize() - memRange.offset);
(*m_hAllocator->GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hAllocator->m_hDevice, 1, &memRange);
}
}
}
// Go over all blocks in reverse order. Unmap those that were mapped just for defragmentation.
// Regardless of res >= 0.
for(size_t blockIndex = blockCount; blockIndex--; )
{
const BlockInfo& currBlockInfo = blockInfo[blockIndex];
if((currBlockInfo.flags & BLOCK_FLAG_MAPPED_FOR_DEFRAGMENTATION) != 0)
{
VmaDeviceMemoryBlock* pBlock = m_pBlockVector->m_Blocks[blockIndex];
pBlock->Unmap(m_hAllocator, 1);
}
}
return res;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// VmaDefragmentationContext // VmaDefragmentationContext
@ -12879,7 +12816,7 @@ VkResult VmaAllocator_T::DefragmentationBegin(
const size_t poolCount = m_Pools.size(); const size_t poolCount = m_Pools.size();
const VkMemoryPropertyFlags requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; const VkMemoryPropertyFlags requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
// Dispatch pAllocations among defragmentators. Create them in BlockVectors when necessary. // Dispatch pAllocations among defragmentators. Create them when necessary.
for(size_t allocIndex = 0; allocIndex < info.allocationCount; ++allocIndex) for(size_t allocIndex = 0; allocIndex < info.allocationCount; ++allocIndex)
{ {
VmaAllocation hAlloc = info.pAllocations[allocIndex]; VmaAllocation hAlloc = info.pAllocations[allocIndex];
@ -12892,31 +12829,34 @@ VkResult VmaAllocator_T::DefragmentationBegin(
// Lost allocation cannot be defragmented. // Lost allocation cannot be defragmented.
(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)) (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
{ {
VmaBlockVector* pAllocBlockVector = VMA_NULL; VmaBlockVector* pBlockVector = VMA_NULL;
const VmaPool hAllocPool = hAlloc->GetPool(); const VmaPool hAllocPool = hAlloc->GetPool();
// This allocation belongs to custom pool. // This allocation belongs to custom pool.
if(hAllocPool != VK_NULL_HANDLE) if(hAllocPool != VK_NULL_HANDLE)
{ {
// Pools with linear or buddy algorithm are not defragmented. // Pools with algorithm other than default are not defragmented.
if(hAllocPool->m_BlockVector.GetAlgorithm() == 0) if(hAllocPool->m_BlockVector.GetAlgorithm() == 0)
{ {
pAllocBlockVector = &hAllocPool->m_BlockVector; pBlockVector = &hAllocPool->m_BlockVector;
} }
} }
// This allocation belongs to general pool. // This allocation belongs to general pool.
else else
{ {
pAllocBlockVector = m_pBlockVectors[memTypeIndex]; pBlockVector = m_pBlockVectors[memTypeIndex];
} }
if(pAllocBlockVector != VMA_NULL) if(pBlockVector != VMA_NULL)
{ {
VmaDefragmentator* const pDefragmentator = if(!pBlockVector->m_pDefragmentationAlgorithm)
pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex); {
pBlockVector->m_pDefragmentationAlgorithm = vma_new(this, VmaDefragmentationAlgorithm)(
this, pBlockVector, currentFrameIndex);
}
VkBool32* const pChanged = (info.pAllocationsChanged != VMA_NULL) ? VkBool32* const pChanged = (info.pAllocationsChanged != VMA_NULL) ?
&info.pAllocationsChanged[allocIndex] : VMA_NULL; &info.pAllocationsChanged[allocIndex] : VMA_NULL;
pDefragmentator->AddAllocation(hAlloc, pChanged); pBlockVector->m_pDefragmentationAlgorithm->AddAllocation(hAlloc, pChanged);
} }
} }
} }