mirror of
https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
synced 2025-01-11 09:40:06 +00:00
Refactored defragmentation code. Removed class VmaDefragmentator. Renamed struct to VmaDefragmentationMove.
Minor fixes in documentation and comments.
This commit is contained in:
parent
ef6cc40b59
commit
a114419b23
@ -1447,7 +1447,7 @@ Features deliberately excluded from the scope of this library:
|
||||
yourself. Any explicit support for sparse binding/residency would rather
|
||||
require another, higher-level library on top of VMA.
|
||||
- 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
|
||||
explicit memory type index and dedicated allocation anyway, so they don't
|
||||
interact with main features of this library. Such special purpose allocations
|
||||
@ -2600,6 +2600,15 @@ typedef struct VmaDefragmentationInfo2 {
|
||||
`UINT32_MAX` means no limit.
|
||||
*/
|
||||
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;
|
||||
|
||||
/** \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
|
||||
@ -5465,6 +5483,9 @@ struct VmaBlockVector
|
||||
{
|
||||
VMA_CLASS_NO_COPY(VmaBlockVector)
|
||||
public:
|
||||
// Used temporarily during defragmentation.
|
||||
VmaDefragmentationAlgorithm* m_pDefragmentationAlgorithm;
|
||||
|
||||
VmaBlockVector(
|
||||
VmaAllocator hAllocator,
|
||||
uint32_t memoryTypeIndex,
|
||||
@ -5515,11 +5536,7 @@ public:
|
||||
size_t* pLostAllocationCount);
|
||||
VkResult CheckCorruption();
|
||||
|
||||
VmaDefragmentator* EnsureDefragmentator(
|
||||
VmaAllocator hAllocator,
|
||||
uint32_t currentFrameIndex);
|
||||
|
||||
// If m_pDefragmentator is not null, uses it to defragment and destroys it.
|
||||
// If m_pDefragmentationAlgorithm is not null, uses it to defragment and destroys it.
|
||||
VkResult Defragment(
|
||||
VmaDefragmentationStats* pDefragmentationStats,
|
||||
VkDeviceSize& maxBytesToMove,
|
||||
@ -5527,7 +5544,6 @@ public:
|
||||
|
||||
private:
|
||||
friend class VmaDefragmentationAlgorithm;
|
||||
friend class VmaDefragmentator;
|
||||
|
||||
const VmaAllocator m_hAllocator;
|
||||
const uint32_t m_MemoryTypeIndex;
|
||||
@ -5539,14 +5555,13 @@ private:
|
||||
const bool m_IsCustomPool;
|
||||
const bool m_ExplicitBlockSize;
|
||||
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;
|
||||
VMA_RW_MUTEX m_Mutex;
|
||||
// Incrementally sorted by sumFreeSize, ascending.
|
||||
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;
|
||||
|
||||
VkDeviceSize CalcMaxBlockSize() const;
|
||||
@ -5597,19 +5612,17 @@ private:
|
||||
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
|
||||
{
|
||||
VMA_CLASS_NO_COPY(VmaDefragmentationAlgorithm)
|
||||
public:
|
||||
struct Move
|
||||
{
|
||||
size_t srcBlockIndex;
|
||||
size_t dstBlockIndex;
|
||||
VkDeviceSize srcOffset;
|
||||
VkDeviceSize dstOffset;
|
||||
VkDeviceSize size;
|
||||
};
|
||||
|
||||
VmaDefragmentationAlgorithm(
|
||||
VmaAllocator hAllocator,
|
||||
VmaBlockVector* pBlockVector,
|
||||
@ -5619,7 +5632,7 @@ public:
|
||||
void AddAllocation(VmaAllocation hAlloc, VkBool32* pChanged);
|
||||
|
||||
VkResult Defragment(
|
||||
VmaVector< Move, VmaStlAllocator<Move> >& moves,
|
||||
VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
|
||||
VkDeviceSize maxBytesToMove,
|
||||
uint32_t maxAllocationsToMove);
|
||||
|
||||
@ -5723,7 +5736,7 @@ private:
|
||||
BlockInfoVector m_Blocks;
|
||||
|
||||
VkResult DefragmentRound(
|
||||
VmaVector< Move, VmaStlAllocator<Move> >& moves,
|
||||
VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
|
||||
VkDeviceSize maxBytesToMove,
|
||||
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
|
||||
{
|
||||
VMA_CLASS_NO_COPY(VmaDefragmentationContext_T)
|
||||
@ -10378,6 +10364,7 @@ VmaBlockVector::VmaBlockVector(
|
||||
bool isCustomPool,
|
||||
bool explicitBlockSize,
|
||||
uint32_t algorithm) :
|
||||
m_pDefragmentationAlgorithm(VMA_NULL),
|
||||
m_hAllocator(hAllocator),
|
||||
m_MemoryTypeIndex(memoryTypeIndex),
|
||||
m_PreferredBlockSize(preferredBlockSize),
|
||||
@ -10390,14 +10377,13 @@ VmaBlockVector::VmaBlockVector(
|
||||
m_Algorithm(algorithm),
|
||||
m_HasEmptyBlock(false),
|
||||
m_Blocks(VmaStlAllocator<VmaDeviceMemoryBlock*>(hAllocator->GetAllocationCallbacks())),
|
||||
m_pDefragmentator(VMA_NULL),
|
||||
m_NextBlockId(0)
|
||||
{
|
||||
}
|
||||
|
||||
VmaBlockVector::~VmaBlockVector()
|
||||
{
|
||||
VMA_ASSERT(m_pDefragmentator == VMA_NULL);
|
||||
VMA_ASSERT(m_pDefragmentationAlgorithm == VMA_NULL);
|
||||
|
||||
for(size_t i = m_Blocks.size(); i--; )
|
||||
{
|
||||
@ -11112,27 +11098,12 @@ void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json)
|
||||
|
||||
#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(
|
||||
VmaDefragmentationStats* pDefragmentationStats,
|
||||
VkDeviceSize& maxBytesToMove,
|
||||
uint32_t& maxAllocationsToMove)
|
||||
{
|
||||
if(m_pDefragmentator == VMA_NULL)
|
||||
if(!m_pDefragmentationAlgorithm)
|
||||
{
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
@ -11140,13 +11111,134 @@ VkResult VmaBlockVector::Defragment(
|
||||
VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex);
|
||||
|
||||
// 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.
|
||||
if(pDefragmentationStats != VMA_NULL)
|
||||
{
|
||||
const VkDeviceSize bytesMoved = m_pDefragmentator->GetBytesMoved();
|
||||
const uint32_t allocationsMoved = m_pDefragmentator->GetAllocationsMoved();
|
||||
const VkDeviceSize bytesMoved = m_pDefragmentationAlgorithm->GetBytesMoved();
|
||||
const uint32_t allocationsMoved = m_pDefragmentationAlgorithm->GetAllocationsMoved();
|
||||
pDefragmentationStats->bytesMoved += bytesMoved;
|
||||
pDefragmentationStats->allocationsMoved += allocationsMoved;
|
||||
VMA_ASSERT(bytesMoved <= maxBytesToMove);
|
||||
@ -11156,7 +11248,7 @@ VkResult VmaBlockVector::Defragment(
|
||||
}
|
||||
|
||||
// Free empty blocks.
|
||||
if(result >= 0)
|
||||
if(res >= 0)
|
||||
{
|
||||
m_HasEmptyBlock = false;
|
||||
for(size_t blockIndex = m_Blocks.size(); blockIndex--; )
|
||||
@ -11184,11 +11276,11 @@ VkResult VmaBlockVector::Defragment(
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy defragmentator.
|
||||
vma_delete(m_hAllocator, m_pDefragmentator);
|
||||
m_pDefragmentator = VMA_NULL;
|
||||
// Destroy defragmentation algorithm object.
|
||||
vma_delete(m_hAllocator, m_pDefragmentationAlgorithm);
|
||||
m_pDefragmentationAlgorithm = VMA_NULL;
|
||||
|
||||
return result;
|
||||
return res;
|
||||
}
|
||||
|
||||
void VmaBlockVector::MakePoolAllocationsLost(
|
||||
@ -11284,7 +11376,7 @@ void VmaDefragmentationAlgorithm::AddAllocation(VmaAllocation hAlloc, VkBool32*
|
||||
}
|
||||
|
||||
VkResult VmaDefragmentationAlgorithm::DefragmentRound(
|
||||
VmaVector< Move, VmaStlAllocator<Move> >& moves,
|
||||
VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
|
||||
VkDeviceSize maxBytesToMove,
|
||||
uint32_t maxAllocationsToMove)
|
||||
{
|
||||
@ -11357,7 +11449,7 @@ VkResult VmaDefragmentationAlgorithm::DefragmentRound(
|
||||
return VK_INCOMPLETE;
|
||||
}
|
||||
|
||||
Move move;
|
||||
VmaDefragmentationMove move;
|
||||
move.srcBlockIndex = pSrcBlockInfo->m_OriginalBlockIndex;
|
||||
move.dstBlockIndex = pDstBlockInfo->m_OriginalBlockIndex;
|
||||
move.srcOffset = srcOffset;
|
||||
@ -11411,7 +11503,7 @@ VkResult VmaDefragmentationAlgorithm::DefragmentRound(
|
||||
}
|
||||
|
||||
VkResult VmaDefragmentationAlgorithm::Defragment(
|
||||
VmaVector< Move, VmaStlAllocator<Move> >& moves,
|
||||
VmaVector< VmaDefragmentationMove, VmaStlAllocator<VmaDefragmentationMove> >& moves,
|
||||
VkDeviceSize maxBytesToMove,
|
||||
uint32_t maxAllocationsToMove)
|
||||
{
|
||||
@ -11493,161 +11585,6 @@ bool VmaDefragmentationAlgorithm::MoveMakesSense(
|
||||
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
|
||||
|
||||
@ -12879,7 +12816,7 @@ VkResult VmaAllocator_T::DefragmentationBegin(
|
||||
const size_t poolCount = m_Pools.size();
|
||||
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)
|
||||
{
|
||||
VmaAllocation hAlloc = info.pAllocations[allocIndex];
|
||||
@ -12892,31 +12829,34 @@ VkResult VmaAllocator_T::DefragmentationBegin(
|
||||
// Lost allocation cannot be defragmented.
|
||||
(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
|
||||
{
|
||||
VmaBlockVector* pAllocBlockVector = VMA_NULL;
|
||||
VmaBlockVector* pBlockVector = VMA_NULL;
|
||||
|
||||
const VmaPool hAllocPool = hAlloc->GetPool();
|
||||
// This allocation belongs to custom pool.
|
||||
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)
|
||||
{
|
||||
pAllocBlockVector = &hAllocPool->m_BlockVector;
|
||||
pBlockVector = &hAllocPool->m_BlockVector;
|
||||
}
|
||||
}
|
||||
// This allocation belongs to general pool.
|
||||
else
|
||||
{
|
||||
pAllocBlockVector = m_pBlockVectors[memTypeIndex];
|
||||
pBlockVector = m_pBlockVectors[memTypeIndex];
|
||||
}
|
||||
|
||||
if(pAllocBlockVector != VMA_NULL)
|
||||
if(pBlockVector != VMA_NULL)
|
||||
{
|
||||
VmaDefragmentator* const pDefragmentator =
|
||||
pAllocBlockVector->EnsureDefragmentator(this, currentFrameIndex);
|
||||
if(!pBlockVector->m_pDefragmentationAlgorithm)
|
||||
{
|
||||
pBlockVector->m_pDefragmentationAlgorithm = vma_new(this, VmaDefragmentationAlgorithm)(
|
||||
this, pBlockVector, currentFrameIndex);
|
||||
}
|
||||
VkBool32* const pChanged = (info.pAllocationsChanged != VMA_NULL) ?
|
||||
&info.pAllocationsChanged[allocIndex] : VMA_NULL;
|
||||
pDefragmentator->AddAllocation(hAlloc, pChanged);
|
||||
pBlockVector->m_pDefragmentationAlgorithm->AddAllocation(hAlloc, pChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user