Defragmentation: added support for memory that is HOST_VISIBLE but not HOST_COHERENT - calling Invalidate and Flush internally.

Fixed a bug in VmaDefragmentator::Defragment.
This commit is contained in:
Adam Sawicki 2018-10-16 14:20:47 +02:00
parent f2975346ef
commit ef6cc40b59

View file

@ -714,7 +714,7 @@ allocations.
To mitigate this problem, you can use vmaDefragment(). Given set of allocations, To mitigate this problem, you can use vmaDefragment(). Given set of allocations,
this function can move them to compact used memory, ensure more continuous free this function can move them to compact used memory, ensure more continuous free
space and possibly also free some `VkDeviceMemory`. Currently it can work only on space and possibly also free some `VkDeviceMemory`. Currently it can work only on
allocations made from memory type that is `HOST_VISIBLE` and `HOST_COHERENT`. Allocations are allocations made from memory type that is `HOST_VISIBLE`. Allocations are
modified to point to the new `VkDeviceMemory` and offset. Data in this memory is modified to point to the new `VkDeviceMemory` and offset. Data in this memory is
also `memmove`-ed to the new place. However, if you have images or buffers bound also `memmove`-ed to the new place. However, if you have images or buffers bound
to these allocations (and you certainly do), you need to destroy, recreate, and to these allocations (and you certainly do), you need to destroy, recreate, and
@ -11534,6 +11534,7 @@ VkResult VmaDefragmentator::Defragment(
} }
const size_t blockCount = m_pBlockVector->m_Blocks.size(); const size_t blockCount = m_pBlockVector->m_Blocks.size();
const bool isNonCoherent = m_hAllocator->IsMemoryTypeNonCoherent(m_pBlockVector->GetMemoryTypeIndex());
enum BLOCK_FLAG enum BLOCK_FLAG
{ {
@ -11582,6 +11583,9 @@ VkResult VmaDefragmentator::Defragment(
// Go over all moves. Do actual data transfer. // Go over all moves. Do actual data transfer.
if(res >= 0) 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) for(size_t moveIndex = 0; moveIndex < moveCount; ++moveIndex)
{ {
const Move& move = moves[moveIndex]; const Move& move = moves[moveIndex];
@ -11591,17 +11595,41 @@ VkResult VmaDefragmentator::Defragment(
VMA_ASSERT(srcBlockInfo.pMappedData && dstBlockInfo.pMappedData); 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. // THE PLACE WHERE ACTUAL DATA COPY HAPPENS.
memcpy( memcpy(
reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset, reinterpret_cast<char*>(dstBlockInfo.pMappedData) + move.dstOffset,
reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset, reinterpret_cast<char*>(srcBlockInfo.pMappedData) + move.srcOffset,
static_cast<size_t>(move.size)); static_cast<size_t>(move.size));
if(VMA_DEBUG_MARGIN > 0) if(m_pBlockVector->IsCorruptionDetectionEnabled())
{ {
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN); VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset - VMA_DEBUG_MARGIN);
VmaWriteMagicValue(dstBlockInfo.pMappedData, move.dstOffset + move.size); 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);
}
} }
} }
@ -12849,6 +12877,7 @@ VkResult VmaAllocator_T::DefragmentationBegin(
VmaMutexLockRead poolsLock(m_PoolsMutex, m_UseMutex); VmaMutexLockRead poolsLock(m_PoolsMutex, m_UseMutex);
const size_t poolCount = m_Pools.size(); 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 in BlockVectors when necessary.
for(size_t allocIndex = 0; allocIndex < info.allocationCount; ++allocIndex) for(size_t allocIndex = 0; allocIndex < info.allocationCount; ++allocIndex)
@ -12857,9 +12886,8 @@ VkResult VmaAllocator_T::DefragmentationBegin(
VMA_ASSERT(hAlloc); VMA_ASSERT(hAlloc);
const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex(); const uint32_t memTypeIndex = hAlloc->GetMemoryTypeIndex();
// DedicatedAlloc cannot be defragmented. // DedicatedAlloc cannot be defragmented.
const VkMemoryPropertyFlags requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) && if((hAlloc->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK) &&
// Only HOST_VISIBLE and HOST_COHERENT memory types can be defragmented. // Only HOST_VISIBLE memory types can be defragmented.
((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags) && ((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags) &&
// Lost allocation cannot be defragmented. // Lost allocation cannot be defragmented.
(hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST)) (hAlloc->GetLastUseFrameIndex() != VMA_FRAME_INDEX_LOST))
@ -12906,7 +12934,7 @@ VkResult VmaAllocator_T::DefragmentationBegin(
++memTypeIndex) ++memTypeIndex)
{ {
// Only HOST_VISIBLE memory types can be defragmented. // Only HOST_VISIBLE memory types can be defragmented.
if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags)
{ {
result = m_pBlockVectors[memTypeIndex]->Defragment( result = m_pBlockVectors[memTypeIndex]->Defragment(
pStats, pStats,