KhronosGroup / Vulkan-ValidationLayers

Vulkan Validation Layers (VVL)
https://vulkan.lunarg.com/doc/sdk/latest/linux/khronos_validation_layer.html
Other
751 stars 402 forks source link

Image layouts are not tracked correctly with dynamic rendering local read #7776

Open ziga-lunarg opened 6 months ago

ziga-lunarg commented 6 months ago

The CTS test dEQP-VK.dynamic_rendering.primary_cmd_buff.local_read.max_input_attachments falsely triggers VUID-vkCmdPipelineBarrier-image-09555.

Here is a test to reproduce:

TEST_F(PositiveDynamicRenderingLocalRead, ImageBarriers) {
    TEST_DESCRIPTION("Test using image barriers inside of a render pass instance");
    AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::synchronization2);
    RETURN_IF_SKIP(InitBasicDynamicRenderingLocalRead());

    vkt::Image image(*m_device, 32, 32, 1, VK_FORMAT_R8G8B8A8_UNORM,
                      VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
                          VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
    vkt::ImageView imageView = image.CreateView();

    VkRenderingAttachmentInfoKHR color_attachment = vku::InitStructHelper();
    color_attachment.imageLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR;
    color_attachment.imageView = imageView;

    VkRenderingInfoKHR begin_rendering_info = vku::InitStructHelper();
    begin_rendering_info.colorAttachmentCount = 1;
    begin_rendering_info.pColorAttachments = &color_attachment;
    begin_rendering_info.layerCount = 1;
    begin_rendering_info.renderArea = {{0, 0}, {32, 32}};

    VkImageMemoryBarrier pre_barrier = vku::InitStructHelper();
    pre_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    pre_barrier.srcAccessMask = 0;
    pre_barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    pre_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    pre_barrier.newLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR;
    pre_barrier.image = image.handle();
    pre_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};

    VkImageMemoryBarrier post_barrier = vku::InitStructHelper();
    post_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    post_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    post_barrier.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
    post_barrier.oldLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR;
    post_barrier.newLayout = VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR;
    post_barrier.image = image.handle();
    post_barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u};

    m_commandBuffer->begin();
    vk::CmdPipelineBarrier(*m_commandBuffer, 0, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                           0, 0, nullptr, 0, nullptr, 1, &pre_barrier);
    m_commandBuffer->BeginRendering(begin_rendering_info);
    vk::CmdPipelineBarrier(*m_commandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                           VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 0, nullptr, 1, &post_barrier);
    m_commandBuffer->EndRendering();
    m_commandBuffer->end();
}
artem-lunarg commented 6 months ago

@ziga-lunarg thanks for the test!

artem-lunarg commented 6 months ago

Repro case commit: https://github.com/artem-lunarg/Vulkan-ValidationLayers/commit/1d1fe9dc7c58d7d2b8a4a656bdb47e18c9c4383c

jmoguill commented 4 months ago

I'm trying to use dynamic rendering local read, and I think I encounter the same issue? "image layout is VK_IMAGE_LAYOUT_UNDEFINED"...."If vkCmdPipelineBarrier2 is called within a render pass instance started with vkCmdBeginRendering"..."it must be in the VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR"....

Using the following code version:

commit c1fc23ca9b42e766addeb6e5e552d1bb4f79c6f9 (HEAD -> main, origin/main, origin/HEAD) Author: spencer-lunarg spencer@lunarg.com Date: Thu Apr 25 19:22:08 2024 +0900

layers: Don't count ImageQuery as image accesses

Digging through the code, in cc_validation.cpp: ' for (const auto &entry : *image_view_image_state->layout_range_map) { if (entry.second != VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ_KHR && entry.second != VK_IMAGE_LAYOUT_GENERAL) { const auto &vuid = sync_vuid_maps::GetShaderTileImageVUID( barrier_loc, sync_vuid_maps::ShaderTileImageError::kShaderTileImageLayout); skip |= LogError(vuid, img_barrier.image, barrier_loc, "image layout is %s.", string_VkImageLayout(entry.second)); } } '

it seems that layout_range_map only gets updated when command buffer is executed?
But the layout check above gets executed before the command buffer executes (when the user calls pipelineBarrier)? So maybe the layout state is out of sync with regards to recording time vs command buffer execution time? (e.g. the layout state is in GPU timeline, while the check is performed in CPU timeline?)