Reputation: 78
I am doing a simple program with vulkan, just to get started. I am clearing the back color, that's it. The problem is that each frame the program allocates more and more memory and I have no clue where is coming from.
bool VulkanRenderer::Update()
{
PrepareFrame(); ///--- < Commenting this
SubmitFrame(); ///--- < and this avoids memory leak
}//Update
This are the other two functions that when they are not called, the program's memory stays still.
void VulkanRenderer::PrepareFrame()
{
///--- Reset command buffers
vkResetCommandPool(m_pDevice, m_pCoreCommandPool, VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
VkResult iRes;
// Get the index of the next available swapchain image:
iRes=m_oSwapChain.AcquireNextImage(m_oSemaphorePresentReady, &m_uSwapChainImage);
if(iRes!=VK_SUCCESS){
CheckVulkanError(iRes);
}
///---------------------------------
/// Convert image to drawable
///---------------------------------
VkCommandBufferBeginInfo oCmdBegin={};
oCmdBegin.sType=VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
///---------------------------------
/// Prepare primary command buffer
///---------------------------------
//vkFreeCommandBuffers(m_pDevice, m_pCoreCommandPool, 1, &m_oPrimaryCmd);
//m_oPrimaryCmd=CreateCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
vkBeginCommandBuffer(m_oPrimaryCmd, &oCmdBegin);
{///--- Convert image to drawable
VkImageMemoryBarrier postPresentBarrier={};
postPresentBarrier.sType=VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
postPresentBarrier.srcAccessMask=VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
postPresentBarrier.dstAccessMask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
postPresentBarrier.oldLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
postPresentBarrier.newLayout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
postPresentBarrier.srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED;
postPresentBarrier.subresourceRange={VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
postPresentBarrier.image=m_oSwapChain.images()[m_uSwapChainImage];
vkCmdPipelineBarrier(m_oPrimaryCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &postPresentBarrier);
}
{///--- Render pass
VkClearValue clearValues[2];
clearValues[0].color={{1.0f, 0.0f, 0.2f, 0.0f}};
clearValues[1].depthStencil={1.0f, 0};
VkRenderPassBeginInfo renderPassBeginInfo={};
renderPassBeginInfo.sType=VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassBeginInfo.renderPass=m_pRenderPass;
renderPassBeginInfo.renderArea.offset.x=0;
renderPassBeginInfo.renderArea.offset.y=0;
renderPassBeginInfo.renderArea.extent.width=m_uSwapchainWidth;
renderPassBeginInfo.renderArea.extent.height=m_uSwapchainHeight;
renderPassBeginInfo.clearValueCount=2;
renderPassBeginInfo.pClearValues=clearValues;
renderPassBeginInfo.framebuffer=m_pFrameBuffers[m_uSwapChainImage];
vkCmdBeginRenderPass(m_oPrimaryCmd, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
vkCmdEndRenderPass(m_oPrimaryCmd);
}
}//PrepareFrame
And
void VulkanRenderer::SubmitFrame()
{
///---------------------------------
/// Executed submited secondary commands
///---------------------------------
vkCmdExecuteCommands(m_oPrimaryCmd, 0, nullptr);
///---------------------------------
/// Convert image to presentable
///---------------------------------
{
VkImageMemoryBarrier prePresentBarrier={};
prePresentBarrier.sType=VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
prePresentBarrier.srcAccessMask=VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
prePresentBarrier.dstAccessMask=VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
prePresentBarrier.oldLayout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
prePresentBarrier.newLayout=VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
prePresentBarrier.srcQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.dstQueueFamilyIndex=VK_QUEUE_FAMILY_IGNORED;
prePresentBarrier.subresourceRange={VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
prePresentBarrier.image=m_oSwapChain.images()[m_uSwapChainImage];
vkCmdPipelineBarrier(m_oPrimaryCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &prePresentBarrier);
}
vkEndCommandBuffer(m_oPrimaryCmd);
///--- Submit
VkPipelineStageFlags wait_dst_stage_mask=VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
VkSubmitInfo submit_info={
VK_STRUCTURE_TYPE_SUBMIT_INFO, // VkStructureType sType
nullptr, // const void *pNext
1, // uint32_t waitSemaphoreCount
&m_oSemaphorePresentReady, // const VkSemaphore *pWaitSemaphores
&wait_dst_stage_mask, // const VkPipelineStageFlags *pWaitDstStageMask;
1, // uint32_t commandBufferCount
&m_oPrimaryCmd, // const VkCommandBuffer *pCommandBuffers
1, // uint32_t signalSemaphoreCount
&m_oSemaphoreRenderComplete // const VkSemaphore *pSignalSemaphores
};
vkQueueSubmit(m_pDeviceQueue, 1, &submit_info, VK_NULL_HANDLE);
///--- Present queue
VkResult iRes;
iRes=m_oSwapChain.QueuePresent(m_pDeviceQueue, m_uSwapChainImage, m_oSemaphoreRenderComplete);
CheckVulkanError(iRes);
///--- Flush device
vkQueueWaitIdle(m_pDeviceQueue);
}//SubmitFrame
Others:
VkResult VulkanSwapchain::AcquireNextImage(VkSemaphore oPresentCompleteSemaphore, uint32_t* pCurrentBuffer)
{
///---------------------------------
/// Acquires next image in the swap chain
///---------------------------------
if(!m_fpAcquireNextImageKHR){
XLOG("%s:%d: m_fpAcquireNextImageKHR", __FUNCTION__, __LINE__);
return VkResult::VK_INCOMPLETE;
}
VkResult iRes=VkResult::VK_SUCCESS;
iRes=m_fpAcquireNextImageKHR(m_pDevice, m_pSwapChain, UINT64_MAX, oPresentCompleteSemaphore, (VkFence)nullptr, pCurrentBuffer);
return iRes;
}//AcquireNextImage
At the beginning I thought it was some Vulkan resource I was allocating and not deallocating, but this is what the Vulkan Debug Layer offers:
INFORMATION: [MEM] Code 0 : Details of Memory Object list (of size 1 elements)
INFORMATION: [MEM] Code 0 : =============================
INFORMATION: [MEM] Code 0 : ===MemObjInfo at 0000000003BD6E58===
INFORMATION: [MEM] Code 0 : Mem object: 0x3bd6ac0
INFORMATION: [MEM] Code 0 : Ref Count: 1
INFORMATION: [MEM] Code 0 : Mem Alloc info:
MEM(INFO): sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
MEM(INFO): pNext = 0000000000000000
MEM(INFO): allocationSize = 2621440
MEM(INFO): memoryTypeIndex = 1
INFORMATION: [MEM] Code 0 : VK OBJECT Binding list of size 1 elements:
INFORMATION: [MEM] Code 0 : VK OBJECT 62742624
INFORMATION: [MEM] Code 0 : VK Command Buffer (CB) binding list of size 0 elements
INFORMATION: [MEM] Code 0 : Details of CB list (of size 1 elements)
INFORMATION: [MEM] Code 0 : ==================
INFORMATION: [MEM] Code 0 : CB Info (0000000003BB4228) has CB 0000000003BB2AD0, fenceId a3, and fence 0
Every single frame there only seem to exists 2 internal resources. I have also overloaded the new/new[]/delete/delete[] operators, and they are not called during the loop.
The examples I have been looking do almost the same, I have tried deleting and creating the primary command buffer each frame, still getting the same result.
Not calling PrepareFrame() and SubmitFrame() fixes the problem. Why?
Where could this allocation come from? How could I hunt down this kind of allocations?
Upvotes: 3
Views: 2035
Reputation: 1
another possible cause to a similiar bug is not calling:
vkDeviceWaitIdle ( Device );
inbetween renders/drawframe calls. All submitted command buffers are added to a list that is only cleared from the:
vkQueueWaitIdle, vkDeviceWaitIdle and vkWaitForFences
calls. If you call one of these in the program, The uniform buffer code will call vkDeviceWaitIdle every frame through the copyBuffer call and it wont't have a leak from vkQueueSubmit() anymore.
Upvotes: -2
Reputation: 5828
As krOoze mentioned, try to update to the latest SDK, or (better) built the layers from source to always get the latest layers. Afaik there is still an open issue for the layers about correct memory checks, so they actually might be causing the memory leaks. Judging from the validation layer names in the source you uploaded you're not using the most recent ones, as e.g. the VK_LAYER_LUNARG_threading one has recently been renamed to VK_LAYER_GOOGLE_threading.
Using the current layers with my examples I can't reproduce any memory leaks.
But one thing that I noticed in your source:
int iUsedLayers=0;
const char* ppLayers[64]={};
if(bExtraLayers){
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_standard_validation";
#if VKMEMDBG
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_mem_tracker";
#endif
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_threading";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_object_tracker";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_draw_state";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_param_checker";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_swapchain";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_device_limits";
ppLayers[iUsedLayers++]="VK_LAYER_LUNARG_image";
ppLayers[iUsedLayers++]="VK_LAYER_GOOGLE_unique_objects";
}
Although it should do no harm, you actually add all the layers (except for the mem_tracker one depending on the define) twice. The VK_LAYER_LUNARG_standard_validation meta layer already enables all available (base) validation layers in the correct order (which might not be the case with your order), so adding them one by one afterwards isn't necessary.
Upvotes: 5