Added support for VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT without HOST_VISIBLE. Improved empty block heuristics.

This commit is contained in:
Adam Sawicki 2017-10-02 14:28:51 +02:00
parent 951f66a841
commit 1bb85fa719
3 changed files with 116 additions and 81 deletions

View File

@ -203,7 +203,8 @@ Functions</h2></td></tr>
</td></tr> </td></tr>
<tr><td class="fieldname"><a id="ggad9889c10c798b040d59c92f257cae597ae443691ef3d077c0dc3de5576ac4c312"></a>VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT&#160;</td><td class="fielddoc"><p>Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. </p> <tr><td class="fieldname"><a id="ggad9889c10c798b040d59c92f257cae597ae443691ef3d077c0dc3de5576ac4c312"></a>VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT&#160;</td><td class="fielddoc"><p>Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. </p>
<p>Pointer to mapped memory will be returned through <a class="el" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2" title="Pointer to the beginning of this allocation as mapped data. Null if this alloaction is not persistent...">VmaAllocationInfo::pMappedData</a>. You cannot map the memory on your own as multiple mappings of a single <code>VkDeviceMemory</code> are illegal.</p> <p>Pointer to mapped memory will be returned through <a class="el" href="struct_vma_allocation_info.html#a5eeffbe2d2f30f53370ff14aefbadbe2" title="Pointer to the beginning of this allocation as mapped data. Null if this alloaction is not persistent...">VmaAllocationInfo::pMappedData</a>. You cannot map the memory on your own as multiple mappings of a single <code>VkDeviceMemory</code> are illegal.</p>
<p>If <a class="el" href="struct_vma_allocation_create_info.html#a6272c0555cfd1fe28bff1afeb6190150" title="Pool that this allocation should be created in. ">VmaAllocationCreateInfo::pool</a> is not null, usage of this flag must match usage of flag <code>VMA_POOL_CREATE_PERSISTENT_MAP_BIT</code> used during pool creation. </p> <p>If <a class="el" href="struct_vma_allocation_create_info.html#a6272c0555cfd1fe28bff1afeb6190150" title="Pool that this allocation should be created in. ">VmaAllocationCreateInfo::pool</a> is not null, usage of this flag must match usage of flag <code>VMA_POOL_CREATE_PERSISTENT_MAP_BIT</code> used during pool creation.</p>
<p>Is it valid to use this flag for allocation made from memory type that is not <code>HOST_VISIBLE</code>. This flag is then ignored and memory is not mapped. This is useful if you need an allocation that is efficient to use on GPU (<code>DEVICE_LOCAL</code>) and still want to map it directly if possible on platforms that support it (e.g. Intel GPU). </p>
</td></tr> </td></tr>
<tr><td class="fieldname"><a id="ggad9889c10c798b040d59c92f257cae597a5f436af6c8fe8540573a6d22627a6fd2"></a>VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT&#160;</td><td class="fielddoc"><p>Allocation created with this flag can become lost as a result of another allocation with <code>VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT</code> flag, so you must check it before use.</p> <tr><td class="fieldname"><a id="ggad9889c10c798b040d59c92f257cae597a5f436af6c8fe8540573a6d22627a6fd2"></a>VMA_ALLOCATION_CREATE_CAN_BECOME_LOST_BIT&#160;</td><td class="fielddoc"><p>Allocation created with this flag can become lost as a result of another allocation with <code>VMA_ALLOCATION_CREATE_CAN_MAKE_OTHER_LOST_BIT</code> flag, so you must check it before use.</p>
<p>To check if allocation is not lost, call <a class="el" href="group__layer2.html#ga86dd08aba8633bfa4ad0df2e76481d8b" title="Returns current information about specified allocation. ">vmaGetAllocationInfo()</a> and check if <a class="el" href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67" title="Handle to Vulkan memory object. ">VmaAllocationInfo::deviceMemory</a> is not <code>VK_NULL_HANDLE</code>.</p> <p>To check if allocation is not lost, call <a class="el" href="group__layer2.html#ga86dd08aba8633bfa4ad0df2e76481d8b" title="Returns current information about specified allocation. ">vmaGetAllocationInfo()</a> and check if <a class="el" href="struct_vma_allocation_info.html#ae0bfb7dfdf79a76ffefc9a94677a2f67" title="Handle to Vulkan memory object. ">VmaAllocationInfo::deviceMemory</a> is not <code>VK_NULL_HANDLE</code>.</p>

File diff suppressed because one or more lines are too long

View File

@ -685,6 +685,12 @@ typedef enum VmaAllocationCreateFlagBits {
If VmaAllocationCreateInfo::pool is not null, usage of this flag must match If VmaAllocationCreateInfo::pool is not null, usage of this flag must match
usage of flag `VMA_POOL_CREATE_PERSISTENT_MAP_BIT` used during pool creation. usage of flag `VMA_POOL_CREATE_PERSISTENT_MAP_BIT` used during pool creation.
Is it valid to use this flag for allocation made from memory type that is not
`HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is
useful if you need an allocation that is efficient to use on GPU
(`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that
support it (e.g. Intel GPU).
*/ */
VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT = 0x00000004, VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT = 0x00000004,
/** Allocation created with this flag can become lost as a result of another /** Allocation created with this flag can become lost as a result of another
@ -5315,8 +5321,8 @@ VkResult VmaBlockVector::Allocate(
VmaAllocation* pAllocation) VmaAllocation* pAllocation)
{ {
// Validate flags. // Validate flags.
if(((createInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0) != if(createInfo.pool != VK_NULL_HANDLE &&
(m_BlockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED)) ((createInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0) != (m_BlockVectorType == VMA_BLOCK_VECTOR_TYPE_MAPPED))
{ {
VMA_ASSERT(0 && "Usage of VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT must match VMA_POOL_CREATE_PERSISTENT_MAP_BIT."); VMA_ASSERT(0 && "Usage of VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT must match VMA_POOL_CREATE_PERSISTENT_MAP_BIT.");
return VK_ERROR_OUT_OF_DEVICE_MEMORY; return VK_ERROR_OUT_OF_DEVICE_MEMORY;
@ -5548,7 +5554,19 @@ void VmaBlockVector::Free(
m_HasEmptyBlock = true; m_HasEmptyBlock = true;
} }
} }
// Must be called after srcBlockIndex is used, because later it may become invalid! // pBlock didn't become empty, but we have another empty block - find and free that one.
// (This is optional, heuristics.)
else if(m_HasEmptyBlock)
{
VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back();
if(pLastBlock->m_Metadata.IsEmpty() && m_Blocks.size() > m_MinBlockCount)
{
pBlockToDelete = pLastBlock;
m_Blocks.pop_back();
m_HasEmptyBlock = false;
}
}
IncrementallySortBlocks(); IncrementallySortBlocks();
} }
@ -6313,17 +6331,31 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex][blockVectorType]; VmaBlockVector* const blockVector = m_pBlockVectors[memTypeIndex][blockVectorType];
VMA_ASSERT(blockVector); VMA_ASSERT(blockVector);
const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize(); VmaAllocationCreateInfo finalCreateInfo = createInfo;
// Heuristics: Allocate own memory if requested size if greater than half of preferred block size.
const bool ownMemory =
(createInfo.flags & VMA_ALLOCATION_CREATE_OWN_MEMORY_BIT) != 0 ||
VMA_DEBUG_ALWAYS_OWN_MEMORY ||
((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
vkMemReq.size > preferredBlockSize / 2);
if(ownMemory) if(VMA_DEBUG_ALWAYS_OWN_MEMORY)
{ {
if((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_OWN_MEMORY_BIT;
}
// Heuristics: Allocate own memory if requested size if greater than half of preferred block size.
const VkDeviceSize preferredBlockSize = blockVector->GetPreferredBlockSize();
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 &&
vkMemReq.size > preferredBlockSize / 2)
{
finalCreateInfo.flags |= VMA_ALLOCATION_CREATE_OWN_MEMORY_BIT;
}
// If memory type is not HOST_VISIBLE, disable PERSISTENT_MAP.
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0 &&
(m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0)
{
finalCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT;
}
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_OWN_MEMORY_BIT) != 0)
{
if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
{ {
return VK_ERROR_OUT_OF_DEVICE_MEMORY; return VK_ERROR_OUT_OF_DEVICE_MEMORY;
} }
@ -6333,8 +6365,8 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
vkMemReq.size, vkMemReq.size,
suballocType, suballocType,
memTypeIndex, memTypeIndex,
(createInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0, (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0,
createInfo.pUserData, finalCreateInfo.pUserData,
pAllocation); pAllocation);
} }
} }
@ -6344,7 +6376,7 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
VK_NULL_HANDLE, // hCurrentPool VK_NULL_HANDLE, // hCurrentPool
m_CurrentFrameIndex.load(), m_CurrentFrameIndex.load(),
vkMemReq, vkMemReq,
createInfo, finalCreateInfo,
suballocType, suballocType,
pAllocation); pAllocation);
if(res == VK_SUCCESS) if(res == VK_SUCCESS)
@ -6353,24 +6385,31 @@ VkResult VmaAllocator_T::AllocateMemoryOfType(
} }
// 5. Try own memory. // 5. Try own memory.
res = AllocateOwnMemory( if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0)
vkMemReq.size,
suballocType,
memTypeIndex,
(createInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0,
createInfo.pUserData,
pAllocation);
if(res == VK_SUCCESS)
{ {
// Succeeded: AllocateOwnMemory function already filld pMemory, nothing more to do here. return VK_ERROR_OUT_OF_DEVICE_MEMORY;
VMA_DEBUG_LOG(" Allocated as OwnMemory");
return VK_SUCCESS;
} }
else else
{ {
// Everything failed: Return error code. res = AllocateOwnMemory(
VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); vkMemReq.size,
return res; suballocType,
memTypeIndex,
(finalCreateInfo.flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0,
finalCreateInfo.pUserData,
pAllocation);
if(res == VK_SUCCESS)
{
// Succeeded: AllocateOwnMemory function already filld pMemory, nothing more to do here.
VMA_DEBUG_LOG(" Allocated as OwnMemory");
return VK_SUCCESS;
}
else
{
// Everything failed: Return error code.
VMA_DEBUG_LOG(" vkAllocateMemory FAILED");
return res;
}
} }
} }
} }
@ -7394,11 +7433,6 @@ VkResult vmaFindMemoryTypeIndex(
break; break;
} }
if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_PERSISTENT_MAP_BIT) != 0)
{
requiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
}
*pMemoryTypeIndex = UINT32_MAX; *pMemoryTypeIndex = UINT32_MAX;
uint32_t minCost = UINT32_MAX; uint32_t minCost = UINT32_MAX;
for(uint32_t memTypeIndex = 0, memTypeBit = 1; for(uint32_t memTypeIndex = 0, memTypeBit = 1;