Eder
Eder

Reputation: 78

Vulkan: Memory leak when rendering

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

Answers (2)

Nolan Beck
Nolan Beck

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

Sascha Willems
Sascha Willems

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

Related Questions