vulkanmemory-barriers

Write to same color attachment from multiple batches


I'm developing a Vulkan app on a laptop with integrated graphics (just one VkQueue) and on start up I need to run a very expensive fragment shader, the output of which is later read as a texture. The problem is, the OS (I believe) sometimes kills it if it takes too long, causing VK_ERROR_DEVICE_LOST.

To get around that, I broke down the rendering work subdividing the draw calls with different scissors/render area, each with its own command buffer and VkSubmitInfo, shared framebuffer. This has fixed the device lost errors, however there is noise/garbage on all scissors but the last. It's not completely random: it can be seen that the draws have been made, but it seems like the driver believes that the memory is available for use after the rendering of a scissor is done.

I need to tell the driver "don't touch this piece of memory", and I assumed this was the point of the framebuffer's render area.

If I set the attachment's initialLayout to READ_ONLY_OPTIMAL (same as finalLayout) instead of UNDEFINED, everything works correctly, but of course I get the validation error that the image is found with the wrong layout. Trying to transition the image into the correct layout breaks the rendering again.

Therefore I assume I'm setting the pipeline barrier incorrectly:

VkImageMemoryBarrier barrier = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
        .image = image,
        .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
        .newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL,
        .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
        .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
        .subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
        .subresourceRange.levelCount = 1,
        .subresourceRange.layerCount = 1,
    };
}
vkCmdPipelineBarrier(cmd[i],
                     VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
                     VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                     0, // dependencyFlags
                     0, NULL, 0, NULL, 1, &barrier);

What I'm doing wrong?

SOLUTION:

renderpass = {.initialLayout=COLOR_ATTACHMENT, .finalLayout=.initialLayout}
if batch == 0
    vkCmdPipelineBarrier(TOP_OF_PIPE, COLOR_ATTACHMENT_OUTPUT,
        barrier={.oldLayout=UNDEFINED, .newLayout=COLOR_ATTACHMENT})

renderpass begin, ..., renderpass end

if batch == last
    vkCmdPipelineBarrier(COLOR_ATTACHMENT_OUTPUT, COLOR_ATTACHMENT_OUTPUT,
        barrier={.oldLayout=COLOR_ATTACHMENT, .newLayout=READ_ONLY})

Solution

  • .oldLayout = VK_IMAGE_LAYOUT_UNDEFINED

    Layout undefined is .. undefined .. it tells the implementation that the data in image can be discarded because it has no way of interpreting the content. You can only use this when you are going to overwrite all the data in image, and in your case that's only true for the first render pass. For the later ones you need to preserve the data and I would keep it in COLOR_ATTACHMENT_OPTIMAL

    .newLayout = VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL

    Seems like the wrong choice for a render pass attachment, given that you are writing to it. Would expect to use COLOR_ATTACHMENT_OPTIMAL for attachments.