KhronosGroup / KhronosGroup/Vulkan-Samples

dynamic_rendering sample wrong barrier for the depth buffer.

Open
#1,334 0 comments 0 reactions 1 assignee View on GitHub

@gpx1000 is already working on this.

Since May 5, 2025.

Dominant language
C++
Stars
5.4k
Forks
835
Avg merge
2h 16m
Merged PRs (30d)
1

Description

I believe you have the wrong barrier in the dynamic_rendering sample. It does not show up, when running the sample directly, since there's a "hard" synchronization with waiting a queue idle involved, but when used in a real-life scenario, the following validation error is reported:

Validation Error: [ SYNC-HAZARD-WRITE-AFTER-WRITE ] | MessageID = 0x5c0ec5d6
vkQueueSubmit2(): WRITE_AFTER_WRITE hazard detected. vkCmdPipelineBarrier2 (from VkCommandBuffer 0x55a6f7cdbfb0 submitted on the current VkQueue 0x55a6f71b09f0[Graphics queue 0.]) writes to VkImage 0xe88693000000000c[Depth buffer image.], which was previously written by vkCmdEndRendering (from VkCommandBuffer 0x55a6f7ce00b0 submitted on VkQueue 0x55a6f71b09f0[Graphics queue 0.]).
No sufficient synchronization is present to ensure that a layout transition does not conflict with a prior write (VK_ACCESS_2_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) at VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT.
Objects: 2
[0] VkQueue 0x55a6f71b09f0[Graphics queue 0.]
[1] VkCommandBuffer 0x55a6f7cdbfb0

In the sample, the barrier is set here, where source stage and access mask are deduced as VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT and 0 respectively.

Here's the standalone dynamic rendering instance from my application using synchronization2 feature. I'm only clearing the images, not doing any actual work:

// render target barrier, not relevant here, so omitted
// ...
// this barrier causes validation error
{
    VkImageSubresourceRange subresourceRange{};
    subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
    subresourceRange.baseMipLevel = 0;
    subresourceRange.levelCount = 1;
    subresourceRange.baseArrayLayer = 0;
    subresourceRange.layerCount = 1;

    VkImageMemoryBarrier2 barrier{};
    barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
    barrier.pNext = nullptr;
    barrier.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;  // VK_PIPELINE_STAGE_2_NONE
    barrier.srcAccessMask = 0;  // VK_ACCESS_NONE
    barrier.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
    barrier.dstAccessMask =
        VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    barrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
    barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
    barrier.image = depthBuffer.getImage();
    barrier.subresourceRange = subresourceRange;

    VkDependencyInfo dependencyInfo{};
    dependencyInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
    dependencyInfo.pNext = nullptr;
    dependencyInfo.dependencyFlags = 0;
    dependencyInfo.memoryBarrierCount = 0;
    dependencyInfo.pMemoryBarriers = nullptr;
    dependencyInfo.bufferMemoryBarrierCount = 0;
    dependencyInfo.pBufferMemoryBarriers = nullptr;
    dependencyInfo.imageMemoryBarrierCount = 1;
    dependencyInfo.pImageMemoryBarriers = &barrier;

    vkCmdPipelineBarrier2(commandBuffer, &dependencyInfo);
}

VkClearColorValue color{};
color.float32[0] = clearColorRgba[0];
color.float32[1] = clearColorRgba[1];
color.float32[2] = clearColorRgba[2];
color.float32[3] = clearColorRgba[3];

VkClearValue colorClearValue{};
colorClearValue.color = color;

VkRenderingAttachmentInfo colorAttachmentInfo{};
colorAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
colorAttachmentInfo.pNext = nullptr;
colorAttachmentInfo.imageView = renderTarget.getImageView();
colorAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
colorAttachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE;
colorAttachmentInfo.resolveImageView = VK_NULL_HANDLE;
colorAttachmentInfo.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentInfo.clearValue = colorClearValue;

VkClearDepthStencilValue depth{};
depth.depth = 1.0f;
depth.stencil = 0;

VkClearValue depthClearValue{};
depthClearValue.depthStencil = depth;

VkRenderingAttachmentInfo depthAttachmentInfo{};
depthAttachmentInfo.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
depthAttachmentInfo.pNext = nullptr;
depthAttachmentInfo.imageView = depthBuffer.getImageView();
depthAttachmentInfo.imageLayout = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL;
depthAttachmentInfo.resolveMode = VK_RESOLVE_MODE_NONE;
depthAttachmentInfo.resolveImageView = VK_NULL_HANDLE;
depthAttachmentInfo.resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthAttachmentInfo.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachmentInfo.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachmentInfo.clearValue = depthClearValue;

VkRect2D renderArea{};
renderArea.offset = VkOffset2D{0, 0};
renderArea.extent = surfaceExtent;

VkRenderingInfo renderingInfo{};
renderingInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
renderingInfo.pNext = nullptr;
renderingInfo.flags = 0;
renderingInfo.renderArea = renderArea;
renderingInfo.layerCount = 1;
renderingInfo.viewMask = 0;
renderingInfo.colorAttachmentCount = 1;
renderingInfo.pColorAttachments = &colorAttachmentInfo;
renderingInfo.pDepthAttachment = &depthAttachmentInfo;
renderingInfo.pStencilAttachment = nullptr;

vkCmdBeginRendering(commandBuffer, &renderingInfo);
vkCmdEndRendering(commandBuffer);

The specification says:

Image layout transitions may perform read and write accesses on all memory bound to the image subresource range, so applications must ensure that all memory writes have been made available before a layout transition is executed.

But the sample explicitly says to not to wait for a previous memory operation with VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT and 0 access.

Btw, there's another issue - if I remove this barrier at all, the validation layers do not report anything.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.