首页 > 解决方案 > vulkan:VkImageMemoryBarrier

问题描述

我不太明白这里。: https ://github.com/SaschaWillems/Vulkan/blob/master/examples/computeshader/computeshader.cpp

void draw()
{
    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));
}

drawCmdBuffers[currentBuffer] 在compute.commandBuffer 之前运行,但是消费者drawCmdBuffers[currentBuffer] 需要生产者compute.commandBuffer 生成的textureComputeTarget。

  1. 我不明白为什么在compute.commandBuffer 之前调用drawCmdBuffers[currentBuffer]。

在下面的代码中,只渲染了第一帧,而右图没有得到textureComputeTarget,所以渲染为蓝色背景。

void draw()

{
    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));

       sleep(1000) // <-------- Wait
}

调用时执行vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)

VkImageMemoryBarrier imageMemoryBarrier = {};
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
// We won't be changing the layout of the image
imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
imageMemoryBarrier.image = textureComputeTarget.image;
imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
vkCmdPipelineBarrier(
    drawCmdBuffers[i],
    VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
    VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
    VK_FLAGS_NONE,
    0, nullptr,
    0, nullptr,
    1, &imageMemoryBarrier);
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
  1. 等待VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,但是这个阶段之前没有执行过,为什么管道没有卡住?是不是因为之前没有管道,所以不需要等待?

在第 6.6 节管道屏障中,vkCmdPipelineBarrier 是一个同步命令,它在提交到同一队列的命令之间或同一子通道中的命令之间插入依赖关系。

void draw()
{
    printf("%p, %p\n", queue, compute.queue);

    VulkanExampleBase::prepareFrame();

    submitInfo.commandBufferCount = 1;
    submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
    VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));

    VulkanExampleBase::submitFrame();

    // Submit compute commands
    // Use a fence to ensure that compute command buffer has finished executin before using it again
    vkWaitForFences(device, 1, &compute.fence, VK_TRUE, UINT64_MAX);
    vkResetFences(device, 1, &compute.fence);

    VkSubmitInfo computeSubmitInfo = vks::initializers::submitInfo();
    computeSubmitInfo.commandBufferCount = 1;
    computeSubmitInfo.pCommandBuffers = &compute.commandBuffer;

    VK_CHECK_RESULT(vkQueueSubmit(compute.queue, 1, &computeSubmitInfo, compute.fence));
    sleep(1000);
}

打印结果:0x6000039c4a20, 0x6000039c4a20 当前队列和compute.queue是同一个队列。但是上面的代码有可能生成不同的队列。

  1. VkImageMemoryBarrier 可以在多个队列中同步吗?

  2. vkCmdPipelineBarrier 是一个同步命令,它在提交到同一队列的命令之间或同一子通道中的命令之间插入依赖关系 。为什么使用“或”,为什么不使用 “和”

标签: vulkan

解决方案


  1. 我不明白为什么在compute.commandBuffer 之前调用drawCmdBuffers[currentBuffer]。

不知道,这是一个例子。作者可能并不十分担心第一帧会发生什么。它只会以一帧延迟绘制。在绘制之前交换计算也应该付出一些努力。

  1. 等待VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,但是这个阶段之前没有执行过,为什么管道没有卡住?是不是因为之前没有管道,所以不需要等待?

因为这不是管道和依赖项的工作方式。vkCmdPipelineBarrier确保在屏障到达(并完成)之前队列中的任何命令\操作至少在到达之后记录的任何命令\操作之前的srcStage阶段(即COMPUTEdstStage

即使之前没有记录任何命令,也满足这种依赖性。即根据“无”的定义,没有尚未达到COMPUTE阶段的命令。

  1. 可以VkImageMemoryBarrier在多个队列中同步吗?

是的,在信号量的帮助下。

对于VK_SHARING_MODE_EXCLUSIVE不同的队列族,它被称为队列族所有权转移(QFOT)。

否则,信号量已经执行了内存依赖,VkImageMemoryBarrier不需要 a。

  1. vkCmdPipelineBarrier是一个同步命令,它在提交到同一队列的命令之间或同一子通道中的命令之间插入依赖关系。为什么使用“或”,为什么不使用“和”?

vkCmdPipelineBarrier要么在子通道之外,然后它与队列中记录的前后命令形成依赖关系。

或者vkCmdPipelineBarrier位于子通道内,在这种情况下,它称为“子通道自依赖”,其范围仅限于该子通道(以及其他限制)。


推荐阅读