Added functions vmaCopyMemoryToAllocation, vmaCopyAllocationToMemory

Few other minor fixes and improvements.
Regenerated documentation.
This commit is contained in:
Adam Sawicki 2024-01-21 13:30:23 +01:00
parent 484f40312f
commit b5456bb4e5
190 changed files with 3464 additions and 5181 deletions

View file

@ -49,6 +49,7 @@ See also: [product page on GPUOpen](https://gpuopen.com/gaming-product/vulkan-me
- [Custom memory pools](@ref choosing_memory_type_custom_memory_pools)
- [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations)
- \subpage memory_mapping
- [Copy functions](@ref memory_mapping_copy_functions)
- [Mapping functions](@ref memory_mapping_mapping_functions)
- [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory)
- [Cache flush and invalidate](@ref memory_mapping_cache_control)
@ -2181,6 +2182,61 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets,
const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes);
/** \brief Maps the allocation temporarily if needed, copies data from specified host pointer to it, and flushes the memory from the host caches if needed.
\param allocator
\param pSrcHostPointer Pointer to the host data that become source of the copy.
\param dstAllocation Handle to the allocation that becomes destination of the copy.
\param dstAllocationLocalOffset Offset within `dstAllocation` where to write copied data, in bytes.
\param size Number of bytes to copy.
This is a convenience function that allows to copy data from a host pointer to an allocation easily.
Same behavior can be achieved by calling vmaMapMemory(), `memcpy()`, vmaUnmapMemory(), vmaFlushAllocation().
This function can be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag.
It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or
#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
Otherwise, the function will fail and generate a Validation Layers error.
`dstAllocationLocalOffset` is relative to the contents of given `dstAllocation`.
If you mean whole allocation, you should pass 0.
Do not pass allocation's offset within device memory block this parameter!
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(
VmaAllocator VMA_NOT_NULL allocator,
const void* VMA_NOT_NULL pSrcHostPointer,
VmaAllocation VMA_NOT_NULL dstAllocation,
VkDeviceSize dstAllocationLocalOffset,
VkDeviceSize size);
/** \brief Invalidates memory in the host caches if needed, maps the allocation temporarily if needed, and copies data from it to a specified host pointer.
\param allocator
\param srcAllocation Handle to the allocation that becomes source of the copy.
\param srcAllocationLocalOffset Offset within `srcAllocation` where to read copied data, in bytes.
\param pDstHostPointer Pointer to the host memory that become destination of the copy.
\param size Number of bytes to copy.
This is a convenience function that allows to copy data from an allocation to a host pointer easily.
Same behavior can be achieved by calling vmaInvalidateAllocation(), vmaMapMemory(), `memcpy()`, vmaUnmapMemory().
This function should be called only for allocations created in a memory type that has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`
and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT` flag.
It can be ensured e.g. by using #VMA_MEMORY_USAGE_AUTO and #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT.
Otherwise, the function may fail and generate a Validation Layers error.
It may also work very slowly when reading from an uncached memory.
`srcAllocationLocalOffset` is relative to the contents of given `srcAllocation`.
If you mean whole allocation, you should pass 0.
Do not pass allocation's offset within device memory block as this parameter!
*/
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(
VmaAllocator VMA_NOT_NULL allocator,
VmaAllocation VMA_NOT_NULL srcAllocation,
VkDeviceSize srcAllocationLocalOffset,
void* VMA_NOT_NULL pDstHostPointer,
VkDeviceSize size);
/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions.
\param allocator
@ -4136,7 +4192,7 @@ public:
--(*m_Atomic);
}
void Commit() { m_Atomic = nullptr; }
void Commit() { m_Atomic = VMA_NULL; }
T Increment(AtomicT* atomic)
{
m_Atomic = atomic;
@ -4144,7 +4200,7 @@ public:
}
private:
AtomicT* m_Atomic = nullptr;
AtomicT* m_Atomic = VMA_NULL;
};
#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT
@ -6210,7 +6266,7 @@ bool VmaDedicatedAllocationList::Validate()
void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats)
{
for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))
{
const VkDeviceSize size = item->GetSize();
inoutStats.statistics.blockCount++;
@ -6227,7 +6283,7 @@ void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats)
inoutStats.blockCount += allocCount;
inoutStats.allocationCount += allocCount;
for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item))
for(auto* item = m_AllocationList.Front(); item != VMA_NULL; item = DedicatedAllocationLinkedList::GetNext(item))
{
const VkDeviceSize size = item->GetSize();
inoutStats.blockBytes += size;
@ -10078,6 +10134,17 @@ public:
const VkDeviceSize* offsets, const VkDeviceSize* sizes,
VMA_CACHE_OPERATION op);
VkResult CopyMemoryToAllocation(
const void* pSrcHostPointer,
VmaAllocation dstAllocation,
VkDeviceSize dstAllocationLocalOffset,
VkDeviceSize size);
VkResult CopyAllocationToMemory(
VmaAllocation srcAllocation,
VkDeviceSize srcAllocationLocalOffset,
void* pDstHostPointer,
VkDeviceSize size);
void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern);
/*
@ -10177,7 +10244,7 @@ private:
VkFlags dedicatedBufferImageUsage,
size_t allocationCount,
VmaAllocation* pAllocations,
const void* pNextChain = nullptr);
const void* pNextChain = VMA_NULL);
void FreeDedicatedMemory(const VmaAllocation allocation);
@ -10352,7 +10419,7 @@ bool VmaDeviceMemoryBlock::Validate() const
VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator)
{
void* pData = nullptr;
void* pData = VMA_NULL;
VkResult res = Map(hAllocator, 1, &pData);
if (res != VK_SUCCESS)
{
@ -14304,6 +14371,43 @@ VkResult VmaAllocator_T::FlushOrInvalidateAllocations(
return res;
}
VkResult VmaAllocator_T::CopyMemoryToAllocation(
const void* pSrcHostPointer,
VmaAllocation dstAllocation,
VkDeviceSize dstAllocationLocalOffset,
VkDeviceSize size)
{
void* dstMappedData = VMA_NULL;
VkResult res = Map(dstAllocation, &dstMappedData);
if(res == VK_SUCCESS)
{
memcpy((char*)dstMappedData + dstAllocationLocalOffset, pSrcHostPointer, (size_t)size);
Unmap(dstAllocation);
res = FlushOrInvalidateAllocation(dstAllocation, dstAllocationLocalOffset, size, VMA_CACHE_FLUSH);
}
return res;
}
VkResult VmaAllocator_T::CopyAllocationToMemory(
VmaAllocation srcAllocation,
VkDeviceSize srcAllocationLocalOffset,
void* pDstHostPointer,
VkDeviceSize size)
{
void* srcMappedData = VMA_NULL;
VkResult res = Map(srcAllocation, &srcMappedData);
if(res == VK_SUCCESS)
{
res = FlushOrInvalidateAllocation(srcAllocation, srcAllocationLocalOffset, size, VMA_CACHE_INVALIDATE);
if(res == VK_SUCCESS)
{
memcpy(pDstHostPointer, (const char*)srcMappedData + srcAllocationLocalOffset, (size_t)size);
Unmap(srcAllocation);
}
}
return res;
}
void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation)
{
VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED);
@ -15405,9 +15509,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation(
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
return res;
return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
@ -15422,9 +15524,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation(
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
return res;
return allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
@ -15447,9 +15547,7 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations(
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
return res;
return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
@ -15472,9 +15570,49 @@ VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations(
VMA_DEBUG_GLOBAL_MUTEX_LOCK
const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
return allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE);
}
return res;
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyMemoryToAllocation(
VmaAllocator allocator,
const void* pSrcHostPointer,
VmaAllocation dstAllocation,
VkDeviceSize dstAllocationLocalOffset,
VkDeviceSize size)
{
VMA_ASSERT(allocator && pSrcHostPointer && dstAllocation);
if(size == 0)
{
return VK_SUCCESS;
}
VMA_DEBUG_LOG("vmaCopyMemoryToAllocation");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->CopyMemoryToAllocation(pSrcHostPointer, dstAllocation, dstAllocationLocalOffset, size);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCopyAllocationToMemory(
VmaAllocator allocator,
VmaAllocation srcAllocation,
VkDeviceSize srcAllocationLocalOffset,
void* pDstHostPointer,
VkDeviceSize size)
{
VMA_ASSERT(allocator && srcAllocation && pDstHostPointer);
if(size == 0)
{
return VK_SUCCESS;
}
VMA_DEBUG_LOG("vmaCopyAllocationToMemory");
VMA_DEBUG_GLOBAL_MUTEX_LOCK
return allocator->CopyAllocationToMemory(srcAllocation, srcAllocationLocalOffset, pDstHostPointer, size);
}
VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption(
@ -16602,6 +16740,7 @@ You can use them directly with memory allocated by this library,
but it is not recommended because of following issue:
Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed.
This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan.
It is also not thread-safe.
Because of this, Vulkan Memory Allocator provides following facilities:
\note If you want to be able to map an allocation, you need to specify one of the flags
@ -16609,11 +16748,44 @@ Because of this, Vulkan Memory Allocator provides following facilities:
in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable
when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values.
For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable,
but they can still be used for consistency.
but these flags can still be used for consistency.
\section memory_mapping_copy_functions Copy functions
The easiest way to copy data from a host pointer to an allocation is to use convenience function vmaCopyMemoryToAllocation().
It automatically maps the Vulkan memory temporarily (if not already mapped), performs `memcpy`,
and calls `vkFlushMappedMemoryRanges` (if required - if memory type is not `HOST_COHERENT`).
It is also the safest one, because using `memcpy` avoids a risk of accidentally introducing memory reads
(e.g. by doing `pMappedVectors[i] += v`), which may be very slow on memory types that are not `HOST_CACHED`.
\code
struct ConstantBuffer
{
...
};
ConstantBuffer constantBufferData = ...
VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
bufCreateInfo.size = sizeof(ConstantBuffer);
bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
VmaAllocationCreateInfo allocCreateInfo = {};
allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO;
allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
VkBuffer buf;
VmaAllocation alloc;
vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr);
vmaCopyMemoryToAllocation(allocator, &constantBufferData, alloc, 0, sizeof(ConstantBuffer));
\endcode
Copy in the other direction - from an allocation to a host pointer can be performed the same way using function vmaCopyAllocationToMemory().
\section memory_mapping_mapping_functions Mapping functions
The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory().
The library provides following functions for mapping of a specific allocation: vmaMapMemory(), vmaUnmapMemory().
They are safer and more convenient to use than standard Vulkan functions.
You can map an allocation multiple times simultaneously - mapping is reference-counted internally.
You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block.